summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/Kconfig32
-rw-r--r--drivers/net/Makefile2
-rw-r--r--drivers/net/bnx2.c229
-rw-r--r--drivers/net/bnx2.h10
-rw-r--r--drivers/net/bonding/bond_3ad.c11
-rw-r--r--drivers/net/bonding/bond_3ad.h2
-rw-r--r--drivers/net/bonding/bond_alb.c5
-rw-r--r--drivers/net/chelsio/Makefile11
-rw-r--r--drivers/net/chelsio/common.h314
-rw-r--r--drivers/net/chelsio/cphy.h148
-rw-r--r--drivers/net/chelsio/cpl5_cmd.h145
-rw-r--r--drivers/net/chelsio/cxgb2.c1256
-rw-r--r--drivers/net/chelsio/elmer0.h151
-rw-r--r--drivers/net/chelsio/espi.c346
-rw-r--r--drivers/net/chelsio/espi.h68
-rw-r--r--drivers/net/chelsio/gmac.h134
-rw-r--r--drivers/net/chelsio/mv88x201x.c252
-rw-r--r--drivers/net/chelsio/pm3393.c826
-rw-r--r--drivers/net/chelsio/regs.h468
-rw-r--r--drivers/net/chelsio/sge.c1684
-rw-r--r--drivers/net/chelsio/sge.h105
-rw-r--r--drivers/net/chelsio/subr.c812
-rw-r--r--drivers/net/chelsio/suni1x10gexp_regs.h213
-rw-r--r--drivers/net/e100.c241
-rw-r--r--drivers/net/hamradio/bpqether.c4
-rw-r--r--drivers/net/ibmveth.c2
-rw-r--r--drivers/net/iseries_veth.c871
-rw-r--r--drivers/net/iseries_veth.h46
-rw-r--r--drivers/net/ppp_generic.c1
-rw-r--r--drivers/net/pppoe.c6
-rw-r--r--drivers/net/rrunner.c3
-rw-r--r--drivers/net/s2io.h4
-rw-r--r--drivers/net/shaper.c50
-rw-r--r--drivers/net/sis190.c1843
-rw-r--r--drivers/net/tg3.c325
-rw-r--r--drivers/net/tg3.h10
-rw-r--r--drivers/net/tulip/Kconfig12
-rw-r--r--drivers/net/tulip/Makefile1
-rw-r--r--drivers/net/tulip/de2104x.c2
-rw-r--r--drivers/net/tulip/media.c36
-rw-r--r--drivers/net/tulip/timer.c1
-rw-r--r--drivers/net/tulip/tulip.h8
-rw-r--r--drivers/net/tulip/tulip_core.c35
-rw-r--r--drivers/net/tulip/uli526x.c1749
-rw-r--r--drivers/net/wan/hdlc_generic.c2
-rw-r--r--drivers/net/wan/lapbether.c2
-rw-r--r--drivers/net/wan/sdla_fr.c22
-rw-r--r--drivers/net/wan/syncppp.c2
-rw-r--r--drivers/net/wireless/Kconfig106
-rw-r--r--drivers/net/wireless/Makefile6
-rw-r--r--drivers/net/wireless/airo.c65
-rw-r--r--drivers/net/wireless/atmel.c62
-rw-r--r--drivers/net/wireless/hostap/Kconfig73
-rw-r--r--drivers/net/wireless/hostap/Makefile5
-rw-r--r--drivers/net/wireless/hostap/hostap.c1198
-rw-r--r--drivers/net/wireless/hostap/hostap.h57
-rw-r--r--drivers/net/wireless/hostap/hostap_80211.h96
-rw-r--r--drivers/net/wireless/hostap/hostap_80211_rx.c1091
-rw-r--r--drivers/net/wireless/hostap/hostap_80211_tx.c524
-rw-r--r--drivers/net/wireless/hostap/hostap_ap.c3288
-rw-r--r--drivers/net/wireless/hostap/hostap_ap.h261
-rw-r--r--drivers/net/wireless/hostap/hostap_common.h435
-rw-r--r--drivers/net/wireless/hostap/hostap_config.h55
-rw-r--r--drivers/net/wireless/hostap/hostap_cs.c1030
-rw-r--r--drivers/net/wireless/hostap/hostap_download.c766
-rw-r--r--drivers/net/wireless/hostap/hostap_hw.c3445
-rw-r--r--drivers/net/wireless/hostap/hostap_info.c499
-rw-r--r--drivers/net/wireless/hostap/hostap_ioctl.c4102
-rw-r--r--drivers/net/wireless/hostap/hostap_pci.c473
-rw-r--r--drivers/net/wireless/hostap/hostap_plx.c645
-rw-r--r--drivers/net/wireless/hostap/hostap_proc.c448
-rw-r--r--drivers/net/wireless/hostap/hostap_wlan.h1033
-rw-r--r--drivers/net/wireless/ieee802_11.h78
-rw-r--r--drivers/net/wireless/ipw2100.c8679
-rw-r--r--drivers/net/wireless/ipw2100.h1167
-rw-r--r--drivers/net/wireless/ipw2200.c7353
-rw-r--r--drivers/net/wireless/ipw2200.h1742
-rw-r--r--drivers/net/wireless/orinoco.c11
-rw-r--r--drivers/net/wireless/strip.c2
-rw-r--r--drivers/net/wireless/wavelan_cs.c26
-rw-r--r--drivers/net/wireless/wavelan_cs.h6
-rw-r--r--drivers/net/wireless/wavelan_cs.p.h17
-rw-r--r--drivers/net/wireless/wl3501.h4
-rw-r--r--drivers/net/wireless/wl3501_cs.c11
84 files changed, 50472 insertions, 889 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 79e8aa6f2b9e..7d8bcb38797a 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -447,7 +447,7 @@ config NET_SB1250_MAC
config SGI_IOC3_ETH
bool "SGI IOC3 Ethernet"
- depends on NET_ETHERNET && PCI && SGI_IP27
+ depends on NET_ETHERNET && PCI && SGI_IP27 && BROKEN
select CRC32
select MII
help
@@ -1923,6 +1923,17 @@ config R8169_VLAN
If in doubt, say Y.
+config SIS190
+ tristate "SiS190 gigabit ethernet support"
+ depends on PCI
+ select CRC32
+ select MII
+ ---help---
+ Say Y here if you have a SiS 190 PCI Gigabit Ethernet adapter.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sis190. This is recommended.
+
config SKGE
tristate "New SysKonnect GigaEthernet support (EXPERIMENTAL)"
depends on PCI && EXPERIMENTAL
@@ -2093,6 +2104,25 @@ endmenu
menu "Ethernet (10000 Mbit)"
depends on !UML
+config CHELSIO_T1
+ tristate "Chelsio 10Gb Ethernet support"
+ depends on PCI
+ help
+ This driver supports Chelsio N110 and N210 models 10Gb Ethernet
+ cards. More information about adapter features and performance
+ tuning is in <file:Documentation/networking/cxgb.txt>.
+
+ For general information about Chelsio and our products, visit
+ our website at <http://www.chelsio.com>.
+
+ For customer support, please visit our customer support page at
+ <http://www.chelsio.com/support.htm>.
+
+ Please send feedback to <linux-bugs@chelsio.com>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called cxgb.
+
config IXGB
tristate "Intel(R) PRO/10GbE support"
depends on PCI
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index a369ae284a9a..5baafcd55610 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -9,6 +9,7 @@ endif
obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IXGB) += ixgb/
+obj-$(CONFIG_CHELSIO_T1) += chelsio/
obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
@@ -42,6 +43,7 @@ obj-$(CONFIG_EEPRO100) += eepro100.o
obj-$(CONFIG_E100) += e100.o
obj-$(CONFIG_TLAN) += tlan.o
obj-$(CONFIG_EPIC100) += epic100.o
+obj-$(CONFIG_SIS190) += sis190.o
obj-$(CONFIG_SIS900) += sis900.o
obj-$(CONFIG_YELLOWFIN) += yellowfin.o
obj-$(CONFIG_ACENIC) += acenic.o
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index 8acc655ec1e8..7babf6af4e28 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -14,8 +14,8 @@
#define DRV_MODULE_NAME "bnx2"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "1.2.19"
-#define DRV_MODULE_RELDATE "May 23, 2005"
+#define DRV_MODULE_VERSION "1.2.20"
+#define DRV_MODULE_RELDATE "August 22, 2005"
#define RUN_AT(x) (jiffies + (x))
@@ -52,7 +52,6 @@ static struct {
{ "HP NC370i Multifunction Gigabit Server Adapter" },
{ "Broadcom NetXtreme II BCM5706 1000Base-SX" },
{ "HP NC370F Multifunction Gigabit Server Adapter" },
- { 0 },
};
static struct pci_device_id bnx2_pci_tbl[] = {
@@ -108,6 +107,15 @@ static struct flash_spec flash_table[] =
MODULE_DEVICE_TABLE(pci, bnx2_pci_tbl);
+static inline u32 bnx2_tx_avail(struct bnx2 *bp)
+{
+ u32 diff = TX_RING_IDX(bp->tx_prod) - TX_RING_IDX(bp->tx_cons);
+
+ if (diff > MAX_TX_DESC_CNT)
+ diff = (diff & MAX_TX_DESC_CNT) - 1;
+ return (bp->tx_ring_size - diff);
+}
+
static u32
bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset)
{
@@ -807,7 +815,19 @@ bnx2_setup_serdes_phy(struct bnx2 *bp)
bnx2_write_phy(bp, MII_ADVERTISE, new_adv);
bnx2_write_phy(bp, MII_BMCR, bmcr | BMCR_ANRESTART |
BMCR_ANENABLE);
- bp->serdes_an_pending = SERDES_AN_TIMEOUT / bp->timer_interval;
+ if (CHIP_NUM(bp) == CHIP_NUM_5706) {
+ /* Speed up link-up time when the link partner
+ * does not autonegotiate which is very common
+ * in blade servers. Some blade servers use
+ * IPMI for kerboard input and it's important
+ * to minimize link disruptions. Autoneg. involves
+ * exchanging base pages plus 3 next pages and
+ * normally completes in about 120 msec.
+ */
+ bp->current_interval = SERDES_AN_TIMEOUT;
+ bp->serdes_an_pending = 1;
+ mod_timer(&bp->timer, jiffies + bp->current_interval);
+ }
}
return 0;
@@ -1327,22 +1347,17 @@ bnx2_tx_int(struct bnx2 *bp)
}
}
- atomic_add(tx_free_bd, &bp->tx_avail_bd);
+ bp->tx_cons = sw_cons;
if (unlikely(netif_queue_stopped(bp->dev))) {
- unsigned long flags;
-
- spin_lock_irqsave(&bp->tx_lock, flags);
+ spin_lock(&bp->tx_lock);
if ((netif_queue_stopped(bp->dev)) &&
- (atomic_read(&bp->tx_avail_bd) > MAX_SKB_FRAGS)) {
+ (bnx2_tx_avail(bp) > MAX_SKB_FRAGS)) {
netif_wake_queue(bp->dev);
}
- spin_unlock_irqrestore(&bp->tx_lock, flags);
+ spin_unlock(&bp->tx_lock);
}
-
- bp->tx_cons = sw_cons;
-
}
static inline void
@@ -1523,15 +1538,12 @@ bnx2_msi(int irq, void *dev_instance, struct pt_regs *regs)
BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
/* Return here if interrupt is disabled. */
- if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
- return IRQ_RETVAL(1);
- }
+ if (unlikely(atomic_read(&bp->intr_sem) != 0))
+ return IRQ_HANDLED;
- if (netif_rx_schedule_prep(dev)) {
- __netif_rx_schedule(dev);
- }
+ netif_rx_schedule(dev);
- return IRQ_RETVAL(1);
+ return IRQ_HANDLED;
}
static irqreturn_t
@@ -1549,22 +1561,19 @@ bnx2_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
if ((bp->status_blk->status_idx == bp->last_status_idx) ||
(REG_RD(bp, BNX2_PCICFG_MISC_STATUS) &
BNX2_PCICFG_MISC_STATUS_INTA_VALUE))
- return IRQ_RETVAL(0);
+ return IRQ_NONE;
REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
BNX2_PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM |
BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
/* Return here if interrupt is shared and is disabled. */
- if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
- return IRQ_RETVAL(1);
- }
+ if (unlikely(atomic_read(&bp->intr_sem) != 0))
+ return IRQ_HANDLED;
- if (netif_rx_schedule_prep(dev)) {
- __netif_rx_schedule(dev);
- }
+ netif_rx_schedule(dev);
- return IRQ_RETVAL(1);
+ return IRQ_HANDLED;
}
static int
@@ -1581,11 +1590,9 @@ bnx2_poll(struct net_device *dev, int *budget)
(bp->status_blk->status_attn_bits_ack &
STATUS_ATTN_BITS_LINK_STATE)) {
- unsigned long flags;
-
- spin_lock_irqsave(&bp->phy_lock, flags);
+ spin_lock(&bp->phy_lock);
bnx2_phy_int(bp);
- spin_unlock_irqrestore(&bp->phy_lock, flags);
+ spin_unlock(&bp->phy_lock);
}
if (bp->status_blk->status_tx_quick_consumer_index0 != bp->tx_cons) {
@@ -1628,9 +1635,8 @@ bnx2_set_rx_mode(struct net_device *dev)
struct bnx2 *bp = dev->priv;
u32 rx_mode, sort_mode;
int i;
- unsigned long flags;
- spin_lock_irqsave(&bp->phy_lock, flags);
+ spin_lock_bh(&bp->phy_lock);
rx_mode = bp->rx_mode & ~(BNX2_EMAC_RX_MODE_PROMISCUOUS |
BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG);
@@ -1691,7 +1697,7 @@ bnx2_set_rx_mode(struct net_device *dev)
REG_WR(bp, BNX2_RPM_SORT_USER0, sort_mode);
REG_WR(bp, BNX2_RPM_SORT_USER0, sort_mode | BNX2_RPM_SORT_USER0_ENA);
- spin_unlock_irqrestore(&bp->phy_lock, flags);
+ spin_unlock_bh(&bp->phy_lock);
}
static void
@@ -2960,7 +2966,6 @@ bnx2_init_tx_ring(struct bnx2 *bp)
bp->tx_prod = 0;
bp->tx_cons = 0;
bp->tx_prod_bseq = 0;
- atomic_set(&bp->tx_avail_bd, bp->tx_ring_size);
val = BNX2_L2CTX_TYPE_TYPE_L2;
val |= BNX2_L2CTX_TYPE_SIZE_L2;
@@ -3507,11 +3512,11 @@ bnx2_test_registers(struct bnx2 *bp)
rw_mask = reg_tbl[i].rw_mask;
ro_mask = reg_tbl[i].ro_mask;
- save_val = readl((u8 *) bp->regview + offset);
+ save_val = readl(bp->regview + offset);
- writel(0, (u8 *) bp->regview + offset);
+ writel(0, bp->regview + offset);
- val = readl((u8 *) bp->regview + offset);
+ val = readl(bp->regview + offset);
if ((val & rw_mask) != 0) {
goto reg_test_err;
}
@@ -3520,9 +3525,9 @@ bnx2_test_registers(struct bnx2 *bp)
goto reg_test_err;
}
- writel(0xffffffff, (u8 *) bp->regview + offset);
+ writel(0xffffffff, bp->regview + offset);
- val = readl((u8 *) bp->regview + offset);
+ val = readl(bp->regview + offset);
if ((val & rw_mask) != rw_mask) {
goto reg_test_err;
}
@@ -3531,11 +3536,11 @@ bnx2_test_registers(struct bnx2 *bp)
goto reg_test_err;
}
- writel(save_val, (u8 *) bp->regview + offset);
+ writel(save_val, bp->regview + offset);
continue;
reg_test_err:
- writel(save_val, (u8 *) bp->regview + offset);
+ writel(save_val, bp->regview + offset);
ret = -ENODEV;
break;
}
@@ -3752,10 +3757,10 @@ bnx2_test_link(struct bnx2 *bp)
{
u32 bmsr;
- spin_lock_irq(&bp->phy_lock);
+ spin_lock_bh(&bp->phy_lock);
bnx2_read_phy(bp, MII_BMSR, &bmsr);
bnx2_read_phy(bp, MII_BMSR, &bmsr);
- spin_unlock_irq(&bp->phy_lock);
+ spin_unlock_bh(&bp->phy_lock);
if (bmsr & BMSR_LSTATUS) {
return 0;
@@ -3801,6 +3806,9 @@ bnx2_timer(unsigned long data)
struct bnx2 *bp = (struct bnx2 *) data;
u32 msg;
+ if (!netif_running(bp->dev))
+ return;
+
if (atomic_read(&bp->intr_sem) != 0)
goto bnx2_restart_timer;
@@ -3809,15 +3817,16 @@ bnx2_timer(unsigned long data)
if ((bp->phy_flags & PHY_SERDES_FLAG) &&
(CHIP_NUM(bp) == CHIP_NUM_5706)) {
- unsigned long flags;
- spin_lock_irqsave(&bp->phy_lock, flags);
+ spin_lock(&bp->phy_lock);
if (bp->serdes_an_pending) {
bp->serdes_an_pending--;
}
else if ((bp->link_up == 0) && (bp->autoneg & AUTONEG_SPEED)) {
u32 bmcr;
+ bp->current_interval = bp->timer_interval;
+
bnx2_read_phy(bp, MII_BMCR, &bmcr);
if (bmcr & BMCR_ANENABLE) {
@@ -3860,14 +3869,14 @@ bnx2_timer(unsigned long data)
}
}
+ else
+ bp->current_interval = bp->timer_interval;
- spin_unlock_irqrestore(&bp->phy_lock, flags);
+ spin_unlock(&bp->phy_lock);
}
bnx2_restart_timer:
- bp->timer.expires = RUN_AT(bp->timer_interval);
-
- add_timer(&bp->timer);
+ mod_timer(&bp->timer, jiffies + bp->current_interval);
}
/* Called with rtnl_lock */
@@ -3920,12 +3929,7 @@ bnx2_open(struct net_device *dev)
return rc;
}
- init_timer(&bp->timer);
-
- bp->timer.expires = RUN_AT(bp->timer_interval);
- bp->timer.data = (unsigned long) bp;
- bp->timer.function = bnx2_timer;
- add_timer(&bp->timer);
+ mod_timer(&bp->timer, jiffies + bp->current_interval);
atomic_set(&bp->intr_sem, 0);
@@ -3976,12 +3980,17 @@ bnx2_reset_task(void *data)
{
struct bnx2 *bp = data;
+ if (!netif_running(bp->dev))
+ return;
+
+ bp->in_reset_task = 1;
bnx2_netif_stop(bp);
bnx2_init_nic(bp);
atomic_set(&bp->intr_sem, 1);
bnx2_netif_start(bp);
+ bp->in_reset_task = 0;
}
static void
@@ -4041,9 +4050,7 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
u16 prod, ring_prod;
int i;
- if (unlikely(atomic_read(&bp->tx_avail_bd) <
- (skb_shinfo(skb)->nr_frags + 1))) {
-
+ if (unlikely(bnx2_tx_avail(bp) < (skb_shinfo(skb)->nr_frags + 1))) {
netif_stop_queue(dev);
printk(KERN_ERR PFX "%s: BUG! Tx ring full when queue awake!\n",
dev->name);
@@ -4140,8 +4147,6 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
prod = NEXT_TX_BD(prod);
bp->tx_prod_bseq += skb->len;
- atomic_sub(last_frag + 1, &bp->tx_avail_bd);
-
REG_WR16(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BIDX, prod);
REG_WR(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BSEQ, bp->tx_prod_bseq);
@@ -4150,17 +4155,13 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
bp->tx_prod = prod;
dev->trans_start = jiffies;
- if (unlikely(atomic_read(&bp->tx_avail_bd) <= MAX_SKB_FRAGS)) {
- unsigned long flags;
-
- spin_lock_irqsave(&bp->tx_lock, flags);
- if (atomic_read(&bp->tx_avail_bd) <= MAX_SKB_FRAGS) {
- netif_stop_queue(dev);
-
- if (atomic_read(&bp->tx_avail_bd) > MAX_SKB_FRAGS)
- netif_wake_queue(dev);
- }
- spin_unlock_irqrestore(&bp->tx_lock, flags);
+ if (unlikely(bnx2_tx_avail(bp) <= MAX_SKB_FRAGS)) {
+ spin_lock(&bp->tx_lock);
+ netif_stop_queue(dev);
+
+ if (bnx2_tx_avail(bp) > MAX_SKB_FRAGS)
+ netif_wake_queue(dev);
+ spin_unlock(&bp->tx_lock);
}
return NETDEV_TX_OK;
@@ -4173,7 +4174,13 @@ bnx2_close(struct net_device *dev)
struct bnx2 *bp = dev->priv;
u32 reset_code;
- flush_scheduled_work();
+ /* Calling flush_scheduled_work() may deadlock because
+ * linkwatch_event() may be on the workqueue and it will try to get
+ * the rtnl_lock which we are holding.
+ */
+ while (bp->in_reset_task)
+ msleep(1);
+
bnx2_netif_stop(bp);
del_timer_sync(&bp->timer);
if (bp->wol)
@@ -4390,11 +4397,11 @@ bnx2_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
bp->req_line_speed = req_line_speed;
bp->req_duplex = req_duplex;
- spin_lock_irq(&bp->phy_lock);
+ spin_lock_bh(&bp->phy_lock);
bnx2_setup_phy(bp);
- spin_unlock_irq(&bp->phy_lock);
+ spin_unlock_bh(&bp->phy_lock);
return 0;
}
@@ -4464,19 +4471,20 @@ bnx2_nway_reset(struct net_device *dev)
return -EINVAL;
}
- spin_lock_irq(&bp->phy_lock);
+ spin_lock_bh(&bp->phy_lock);
/* Force a link down visible on the other side */
if (bp->phy_flags & PHY_SERDES_FLAG) {
bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK);
- spin_unlock_irq(&bp->phy_lock);
+ spin_unlock_bh(&bp->phy_lock);
msleep(20);
- spin_lock_irq(&bp->phy_lock);
+ spin_lock_bh(&bp->phy_lock);
if (CHIP_NUM(bp) == CHIP_NUM_5706) {
- bp->serdes_an_pending = SERDES_AN_TIMEOUT /
- bp->timer_interval;
+ bp->current_interval = SERDES_AN_TIMEOUT;
+ bp->serdes_an_pending = 1;
+ mod_timer(&bp->timer, jiffies + bp->current_interval);
}
}
@@ -4484,7 +4492,7 @@ bnx2_nway_reset(struct net_device *dev)
bmcr &= ~BMCR_LOOPBACK;
bnx2_write_phy(bp, MII_BMCR, bmcr | BMCR_ANRESTART | BMCR_ANENABLE);
- spin_unlock_irq(&bp->phy_lock);
+ spin_unlock_bh(&bp->phy_lock);
return 0;
}
@@ -4670,11 +4678,11 @@ bnx2_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause)
bp->autoneg &= ~AUTONEG_FLOW_CTRL;
}
- spin_lock_irq(&bp->phy_lock);
+ spin_lock_bh(&bp->phy_lock);
bnx2_setup_phy(bp);
- spin_unlock_irq(&bp->phy_lock);
+ spin_unlock_bh(&bp->phy_lock);
return 0;
}
@@ -4698,7 +4706,7 @@ bnx2_set_rx_csum(struct net_device *dev, u32 data)
#define BNX2_NUM_STATS 45
-struct {
+static struct {
char string[ETH_GSTRING_LEN];
} bnx2_stats_str_arr[BNX2_NUM_STATS] = {
{ "rx_bytes" },
@@ -4750,7 +4758,7 @@ struct {
#define STATS_OFFSET32(offset_name) (offsetof(struct statistics_block, offset_name) / 4)
-unsigned long bnx2_stats_offset_arr[BNX2_NUM_STATS] = {
+static unsigned long bnx2_stats_offset_arr[BNX2_NUM_STATS] = {
STATS_OFFSET32(stat_IfHCInOctets_hi),
STATS_OFFSET32(stat_IfHCInBadOctets_hi),
STATS_OFFSET32(stat_IfHCOutOctets_hi),
@@ -4801,7 +4809,7 @@ unsigned long bnx2_stats_offset_arr[BNX2_NUM_STATS] = {
/* stat_IfHCInBadOctets and stat_Dot3StatsCarrierSenseErrors are
* skipped because of errata.
*/
-u8 bnx2_5706_stats_len_arr[BNX2_NUM_STATS] = {
+static u8 bnx2_5706_stats_len_arr[BNX2_NUM_STATS] = {
8,0,8,8,8,8,8,8,8,8,
4,0,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,
@@ -4811,7 +4819,7 @@ u8 bnx2_5706_stats_len_arr[BNX2_NUM_STATS] = {
#define BNX2_NUM_TESTS 6
-struct {
+static struct {
char string[ETH_GSTRING_LEN];
} bnx2_tests_str_arr[BNX2_NUM_TESTS] = {
{ "register_test (offline)" },
@@ -4910,7 +4918,7 @@ bnx2_get_ethtool_stats(struct net_device *dev,
struct bnx2 *bp = dev->priv;
int i;
u32 *hw_stats = (u32 *) bp->stats_blk;
- u8 *stats_len_arr = 0;
+ u8 *stats_len_arr = NULL;
if (hw_stats == NULL) {
memset(buf, 0, sizeof(u64) * BNX2_NUM_STATS);
@@ -5012,7 +5020,7 @@ static struct ethtool_ops bnx2_ethtool_ops = {
static int
bnx2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data;
+ struct mii_ioctl_data *data = if_mii(ifr);
struct bnx2 *bp = dev->priv;
int err;
@@ -5024,9 +5032,9 @@ bnx2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCGMIIREG: {
u32 mii_regval;
- spin_lock_irq(&bp->phy_lock);
+ spin_lock_bh(&bp->phy_lock);
err = bnx2_read_phy(bp, data->reg_num & 0x1f, &mii_regval);
- spin_unlock_irq(&bp->phy_lock);
+ spin_unlock_bh(&bp->phy_lock);
data->val_out = mii_regval;
@@ -5037,9 +5045,9 @@ bnx2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- spin_lock_irq(&bp->phy_lock);
+ spin_lock_bh(&bp->phy_lock);
err = bnx2_write_phy(bp, data->reg_num & 0x1f, data->val_in);
- spin_unlock_irq(&bp->phy_lock);
+ spin_unlock_bh(&bp->phy_lock);
return err;
@@ -5057,6 +5065,9 @@ bnx2_change_mac_addr(struct net_device *dev, void *p)
struct sockaddr *addr = p;
struct bnx2 *bp = dev->priv;
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EINVAL;
+
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
if (netif_running(dev))
bnx2_set_mac_addr(bp);
@@ -5305,6 +5316,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
bp->stats_ticks = 1000000 & 0xffff00;
bp->timer_interval = HZ;
+ bp->current_interval = HZ;
/* Disable WOL support if we are running on a SERDES chip. */
if (CHIP_BOND_ID(bp) & CHIP_BOND_ID_SERDES_BIT) {
@@ -5328,6 +5340,15 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
bp->req_line_speed = 0;
if (bp->phy_flags & PHY_SERDES_FLAG) {
bp->advertising = ETHTOOL_ALL_FIBRE_SPEED | ADVERTISED_Autoneg;
+
+ reg = REG_RD_IND(bp, HOST_VIEW_SHMEM_BASE +
+ BNX2_PORT_HW_CFG_CONFIG);
+ reg &= BNX2_PORT_HW_CFG_CFG_DFLT_LINK_MASK;
+ if (reg == BNX2_PORT_HW_CFG_CFG_DFLT_LINK_1G) {
+ bp->autoneg = 0;
+ bp->req_line_speed = bp->line_speed = SPEED_1000;
+ bp->req_duplex = DUPLEX_FULL;
+ }
}
else {
bp->advertising = ETHTOOL_ALL_COPPER_SPEED | ADVERTISED_Autoneg;
@@ -5335,11 +5356,17 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
bp->req_flow_ctrl = FLOW_CTRL_RX | FLOW_CTRL_TX;
+ init_timer(&bp->timer);
+ bp->timer.expires = RUN_AT(bp->timer_interval);
+ bp->timer.data = (unsigned long) bp;
+ bp->timer.function = bnx2_timer;
+
return 0;
err_out_unmap:
if (bp->regview) {
iounmap(bp->regview);
+ bp->regview = NULL;
}
err_out_release:
@@ -5454,6 +5481,8 @@ bnx2_remove_one(struct pci_dev *pdev)
struct net_device *dev = pci_get_drvdata(pdev);
struct bnx2 *bp = dev->priv;
+ flush_scheduled_work();
+
unregister_netdev(dev);
if (bp->regview)
@@ -5505,12 +5534,12 @@ bnx2_resume(struct pci_dev *pdev)
}
static struct pci_driver bnx2_pci_driver = {
- name: DRV_MODULE_NAME,
- id_table: bnx2_pci_tbl,
- probe: bnx2_init_one,
- remove: __devexit_p(bnx2_remove_one),
- suspend: bnx2_suspend,
- resume: bnx2_resume,
+ .name = DRV_MODULE_NAME,
+ .id_table = bnx2_pci_tbl,
+ .probe = bnx2_init_one,
+ .remove = __devexit_p(bnx2_remove_one),
+ .suspend = bnx2_suspend,
+ .resume = bnx2_resume,
};
static int __init bnx2_init(void)
diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h
index 8214a2853d0d..9ad3f5740cd8 100644
--- a/drivers/net/bnx2.h
+++ b/drivers/net/bnx2.h
@@ -3841,12 +3841,12 @@ struct bnx2 {
struct status_block *status_blk;
u32 last_status_idx;
- atomic_t tx_avail_bd;
struct tx_bd *tx_desc_ring;
struct sw_bd *tx_buf_ring;
u32 tx_prod_bseq;
u16 tx_prod;
u16 tx_cons;
+ int tx_ring_size;
#ifdef BCM_VLAN
struct vlan_group *vlgrp;
@@ -3872,8 +3872,10 @@ struct bnx2 {
char *name;
int timer_interval;
+ int current_interval;
struct timer_list timer;
struct work_struct reset_task;
+ int in_reset_task;
/* Used to synchronize phy accesses. */
spinlock_t phy_lock;
@@ -3927,7 +3929,6 @@ struct bnx2 {
u16 fw_wr_seq;
u16 fw_drv_pulse_wr_seq;
- int tx_ring_size;
dma_addr_t tx_desc_mapping;
@@ -3985,7 +3986,7 @@ struct bnx2 {
#define PHY_LOOPBACK 2
u8 serdes_an_pending;
-#define SERDES_AN_TIMEOUT (2 * HZ)
+#define SERDES_AN_TIMEOUT (HZ / 3)
u8 mac_addr[8];
@@ -4171,6 +4172,9 @@ struct fw_info {
#define BNX2_PORT_HW_CFG_MAC_LOWER 0x00000054
#define BNX2_PORT_HW_CFG_CONFIG 0x00000058
+#define BNX2_PORT_HW_CFG_CFG_DFLT_LINK_MASK 0x001f0000
+#define BNX2_PORT_HW_CFG_CFG_DFLT_LINK_AN 0x00000000
+#define BNX2_PORT_HW_CFG_CFG_DFLT_LINK_1G 0x00030000
#define BNX2_PORT_HW_CFG_IMD_MAC_A_UPPER 0x00000068
#define BNX2_PORT_HW_CFG_IMD_MAC_A_LOWER 0x0000006c
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index a2e8dda5afac..d2f34d5a8083 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -2419,22 +2419,19 @@ out:
return 0;
}
-int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type* ptype)
+int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type* ptype, struct net_device *orig_dev)
{
struct bonding *bond = dev->priv;
struct slave *slave = NULL;
int ret = NET_RX_DROP;
- if (!(dev->flags & IFF_MASTER)) {
+ if (!(dev->flags & IFF_MASTER))
goto out;
- }
read_lock(&bond->lock);
- slave = bond_get_slave_by_dev((struct bonding *)dev->priv,
- skb->real_dev);
- if (slave == NULL) {
+ slave = bond_get_slave_by_dev((struct bonding *)dev->priv, orig_dev);
+ if (!slave)
goto out_unlock;
- }
bond_3ad_rx_indication((struct lacpdu *) skb->data, slave, skb->len);
diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h
index f46823894187..673a30af5660 100644
--- a/drivers/net/bonding/bond_3ad.h
+++ b/drivers/net/bonding/bond_3ad.h
@@ -295,6 +295,6 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave);
void bond_3ad_handle_link_change(struct slave *slave, char link);
int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info);
int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev);
-int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type* ptype);
+int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type* ptype, struct net_device *orig_dev);
#endif //__BOND_3AD_H__
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 19e829b567d0..f8fce3961197 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -354,15 +354,14 @@ static void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp)
_unlock_rx_hashtbl(bond);
}
-static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct packet_type *ptype)
+static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct packet_type *ptype, struct net_device *orig_dev)
{
struct bonding *bond = bond_dev->priv;
struct arp_pkt *arp = (struct arp_pkt *)skb->data;
int res = NET_RX_DROP;
- if (!(bond_dev->flags & IFF_MASTER)) {
+ if (!(bond_dev->flags & IFF_MASTER))
goto out;
- }
if (!arp) {
dprintk("Packet has no ARP data\n");
diff --git a/drivers/net/chelsio/Makefile b/drivers/net/chelsio/Makefile
new file mode 100644
index 000000000000..91e927827c43
--- /dev/null
+++ b/drivers/net/chelsio/Makefile
@@ -0,0 +1,11 @@
+#
+# Chelsio 10Gb NIC driver for Linux.
+#
+
+obj-$(CONFIG_CHELSIO_T1) += cxgb.o
+
+EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/chelsio $(DEBUG_FLAGS)
+
+
+cxgb-objs := cxgb2.o espi.o pm3393.o sge.o subr.o mv88x201x.o
+
diff --git a/drivers/net/chelsio/common.h b/drivers/net/chelsio/common.h
new file mode 100644
index 000000000000..f09348802b46
--- /dev/null
+++ b/drivers/net/chelsio/common.h
@@ -0,0 +1,314 @@
+/*****************************************************************************
+ * *
+ * File: common.h *
+ * $Revision: 1.21 $ *
+ * $Date: 2005/06/22 00:43:25 $ *
+ * Description: *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#ifndef _CXGB_COMMON_H_
+#define _CXGB_COMMON_H_
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/pci_ids.h>
+
+#define DRV_DESCRIPTION "Chelsio 10Gb Ethernet Driver"
+#define DRV_NAME "cxgb"
+#define DRV_VERSION "2.1.1"
+#define PFX DRV_NAME ": "
+
+#define CH_ERR(fmt, ...) printk(KERN_ERR PFX fmt, ## __VA_ARGS__)
+#define CH_WARN(fmt, ...) printk(KERN_WARNING PFX fmt, ## __VA_ARGS__)
+#define CH_ALERT(fmt, ...) printk(KERN_ALERT PFX fmt, ## __VA_ARGS__)
+
+#define CH_DEVICE(devid, ssid, idx) \
+ { PCI_VENDOR_ID_CHELSIO, devid, PCI_ANY_ID, ssid, 0, 0, idx }
+
+#define SUPPORTED_PAUSE (1 << 13)
+#define SUPPORTED_LOOPBACK (1 << 15)
+
+#define ADVERTISED_PAUSE (1 << 13)
+#define ADVERTISED_ASYM_PAUSE (1 << 14)
+
+typedef struct adapter adapter_t;
+
+void t1_elmer0_ext_intr(adapter_t *adapter);
+void t1_link_changed(adapter_t *adapter, int port_id, int link_status,
+ int speed, int duplex, int fc);
+
+struct t1_rx_mode {
+ struct net_device *dev;
+ u32 idx;
+ struct dev_mc_list *list;
+};
+
+#define t1_rx_mode_promisc(rm) (rm->dev->flags & IFF_PROMISC)
+#define t1_rx_mode_allmulti(rm) (rm->dev->flags & IFF_ALLMULTI)
+#define t1_rx_mode_mc_cnt(rm) (rm->dev->mc_count)
+
+static inline u8 *t1_get_next_mcaddr(struct t1_rx_mode *rm)
+{
+ u8 *addr = 0;
+
+ if (rm->idx++ < rm->dev->mc_count) {
+ addr = rm->list->dmi_addr;
+ rm->list = rm->list->next;
+ }
+ return addr;
+}
+
+#define MAX_NPORTS 4
+
+#define SPEED_INVALID 0xffff
+#define DUPLEX_INVALID 0xff
+
+enum {
+ CHBT_BOARD_N110,
+ CHBT_BOARD_N210
+};
+
+enum {
+ CHBT_TERM_T1,
+ CHBT_TERM_T2
+};
+
+enum {
+ CHBT_MAC_PM3393,
+};
+
+enum {
+ CHBT_PHY_88X2010,
+};
+
+enum {
+ PAUSE_RX = 1 << 0,
+ PAUSE_TX = 1 << 1,
+ PAUSE_AUTONEG = 1 << 2
+};
+
+/* Revisions of T1 chip */
+enum {
+ TERM_T1A = 0,
+ TERM_T1B = 1,
+ TERM_T2 = 3
+};
+
+struct sge_params {
+ unsigned int cmdQ_size[2];
+ unsigned int freelQ_size[2];
+ unsigned int large_buf_capacity;
+ unsigned int rx_coalesce_usecs;
+ unsigned int last_rx_coalesce_raw;
+ unsigned int default_rx_coalesce_usecs;
+ unsigned int sample_interval_usecs;
+ unsigned int coalesce_enable;
+ unsigned int polling;
+};
+
+struct chelsio_pci_params {
+ unsigned short speed;
+ unsigned char width;
+ unsigned char is_pcix;
+};
+
+struct adapter_params {
+ struct sge_params sge;
+ struct chelsio_pci_params pci;
+
+ const struct board_info *brd_info;
+
+ unsigned int nports; /* # of ethernet ports */
+ unsigned int stats_update_period;
+ unsigned short chip_revision;
+ unsigned char chip_version;
+};
+
+struct link_config {
+ unsigned int supported; /* link capabilities */
+ unsigned int advertising; /* advertised capabilities */
+ unsigned short requested_speed; /* speed user has requested */
+ unsigned short speed; /* actual link speed */
+ unsigned char requested_duplex; /* duplex user has requested */
+ unsigned char duplex; /* actual link duplex */
+ unsigned char requested_fc; /* flow control user has requested */
+ unsigned char fc; /* actual link flow control */
+ unsigned char autoneg; /* autonegotiating? */
+};
+
+struct cmac;
+struct cphy;
+
+struct port_info {
+ struct net_device *dev;
+ struct cmac *mac;
+ struct cphy *phy;
+ struct link_config link_config;
+ struct net_device_stats netstats;
+};
+
+struct sge;
+struct peespi;
+
+struct adapter {
+ u8 *regs;
+ struct pci_dev *pdev;
+ unsigned long registered_device_map;
+ unsigned long open_device_map;
+ unsigned long flags;
+
+ const char *name;
+ int msg_enable;
+ u32 mmio_len;
+
+ struct work_struct ext_intr_handler_task;
+ struct adapter_params params;
+
+ struct vlan_group *vlan_grp;
+
+ /* Terminator modules. */
+ struct sge *sge;
+ struct peespi *espi;
+
+ struct port_info port[MAX_NPORTS];
+ struct work_struct stats_update_task;
+ struct timer_list stats_update_timer;
+
+ struct semaphore mib_mutex;
+ spinlock_t tpi_lock;
+ spinlock_t work_lock;
+ /* guards async operations */
+ spinlock_t async_lock ____cacheline_aligned;
+ u32 slow_intr_mask;
+};
+
+enum { /* adapter flags */
+ FULL_INIT_DONE = 1 << 0,
+ TSO_CAPABLE = 1 << 2,
+ TCP_CSUM_CAPABLE = 1 << 3,
+ UDP_CSUM_CAPABLE = 1 << 4,
+ VLAN_ACCEL_CAPABLE = 1 << 5,
+ RX_CSUM_ENABLED = 1 << 6,
+};
+
+struct mdio_ops;
+struct gmac;
+struct gphy;
+
+struct board_info {
+ unsigned char board;
+ unsigned char port_number;
+ unsigned long caps;
+ unsigned char chip_term;
+ unsigned char chip_mac;
+ unsigned char chip_phy;
+ unsigned int clock_core;
+ unsigned int clock_mc3;
+ unsigned int clock_mc4;
+ unsigned int espi_nports;
+ unsigned int clock_cspi;
+ unsigned int clock_elmer0;
+ unsigned char mdio_mdien;
+ unsigned char mdio_mdiinv;
+ unsigned char mdio_mdc;
+ unsigned char mdio_phybaseaddr;
+ struct gmac *gmac;
+ struct gphy *gphy;
+ struct mdio_ops *mdio_ops;
+ const char *desc;
+};
+
+extern struct pci_device_id t1_pci_tbl[];
+
+static inline int adapter_matches_type(const adapter_t *adapter,
+ int version, int revision)
+{
+ return adapter->params.chip_version == version &&
+ adapter->params.chip_revision == revision;
+}
+
+#define t1_is_T1B(adap) adapter_matches_type(adap, CHBT_TERM_T1, TERM_T1B)
+#define is_T2(adap) adapter_matches_type(adap, CHBT_TERM_T2, TERM_T2)
+
+/* Returns true if an adapter supports VLAN acceleration and TSO */
+static inline int vlan_tso_capable(const adapter_t *adapter)
+{
+ return !t1_is_T1B(adapter);
+}
+
+#define for_each_port(adapter, iter) \
+ for (iter = 0; iter < (adapter)->params.nports; ++iter)
+
+#define board_info(adapter) ((adapter)->params.brd_info)
+#define is_10G(adapter) (board_info(adapter)->caps & SUPPORTED_10000baseT_Full)
+
+static inline unsigned int core_ticks_per_usec(const adapter_t *adap)
+{
+ return board_info(adap)->clock_core / 1000000;
+}
+
+extern int t1_tpi_write(adapter_t *adapter, u32 addr, u32 value);
+extern int t1_tpi_read(adapter_t *adapter, u32 addr, u32 *value);
+
+extern void t1_interrupts_enable(adapter_t *adapter);
+extern void t1_interrupts_disable(adapter_t *adapter);
+extern void t1_interrupts_clear(adapter_t *adapter);
+extern int elmer0_ext_intr_handler(adapter_t *adapter);
+extern int t1_slow_intr_handler(adapter_t *adapter);
+
+extern int t1_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc);
+extern const struct board_info *t1_get_board_info(unsigned int board_id);
+extern const struct board_info *t1_get_board_info_from_ids(unsigned int devid,
+ unsigned short ssid);
+extern int t1_seeprom_read(adapter_t *adapter, u32 addr, u32 *data);
+extern int t1_get_board_rev(adapter_t *adapter, const struct board_info *bi,
+ struct adapter_params *p);
+extern int t1_init_hw_modules(adapter_t *adapter);
+extern int t1_init_sw_modules(adapter_t *adapter, const struct board_info *bi);
+extern void t1_free_sw_modules(adapter_t *adapter);
+extern void t1_fatal_err(adapter_t *adapter);
+
+extern void t1_tp_set_udp_checksum_offload(adapter_t *adapter, int enable);
+extern void t1_tp_set_tcp_checksum_offload(adapter_t *adapter, int enable);
+extern void t1_tp_set_ip_checksum_offload(adapter_t *adapter, int enable);
+
+#endif /* _CXGB_COMMON_H_ */
diff --git a/drivers/net/chelsio/cphy.h b/drivers/net/chelsio/cphy.h
new file mode 100644
index 000000000000..3412342f7345
--- /dev/null
+++ b/drivers/net/chelsio/cphy.h
@@ -0,0 +1,148 @@
+/*****************************************************************************
+ * *
+ * File: cphy.h *
+ * $Revision: 1.7 $ *
+ * $Date: 2005/06/21 18:29:47 $ *
+ * Description: *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#ifndef _CXGB_CPHY_H_
+#define _CXGB_CPHY_H_
+
+#include "common.h"
+
+struct mdio_ops {
+ void (*init)(adapter_t *adapter, const struct board_info *bi);
+ int (*read)(adapter_t *adapter, int phy_addr, int mmd_addr,
+ int reg_addr, unsigned int *val);
+ int (*write)(adapter_t *adapter, int phy_addr, int mmd_addr,
+ int reg_addr, unsigned int val);
+};
+
+/* PHY interrupt types */
+enum {
+ cphy_cause_link_change = 0x1,
+ cphy_cause_error = 0x2
+};
+
+struct cphy;
+
+/* PHY operations */
+struct cphy_ops {
+ void (*destroy)(struct cphy *);
+ int (*reset)(struct cphy *, int wait);
+
+ int (*interrupt_enable)(struct cphy *);
+ int (*interrupt_disable)(struct cphy *);
+ int (*interrupt_clear)(struct cphy *);
+ int (*interrupt_handler)(struct cphy *);
+
+ int (*autoneg_enable)(struct cphy *);
+ int (*autoneg_disable)(struct cphy *);
+ int (*autoneg_restart)(struct cphy *);
+
+ int (*advertise)(struct cphy *phy, unsigned int advertise_map);
+ int (*set_loopback)(struct cphy *, int on);
+ int (*set_speed_duplex)(struct cphy *phy, int speed, int duplex);
+ int (*get_link_status)(struct cphy *phy, int *link_ok, int *speed,
+ int *duplex, int *fc);
+};
+
+/* A PHY instance */
+struct cphy {
+ int addr; /* PHY address */
+ adapter_t *adapter; /* associated adapter */
+ struct cphy_ops *ops; /* PHY operations */
+ int (*mdio_read)(adapter_t *adapter, int phy_addr, int mmd_addr,
+ int reg_addr, unsigned int *val);
+ int (*mdio_write)(adapter_t *adapter, int phy_addr, int mmd_addr,
+ int reg_addr, unsigned int val);
+ struct cphy_instance *instance;
+};
+
+/* Convenience MDIO read/write wrappers */
+static inline int mdio_read(struct cphy *cphy, int mmd, int reg,
+ unsigned int *valp)
+{
+ return cphy->mdio_read(cphy->adapter, cphy->addr, mmd, reg, valp);
+}
+
+static inline int mdio_write(struct cphy *cphy, int mmd, int reg,
+ unsigned int val)
+{
+ return cphy->mdio_write(cphy->adapter, cphy->addr, mmd, reg, val);
+}
+
+static inline int simple_mdio_read(struct cphy *cphy, int reg,
+ unsigned int *valp)
+{
+ return mdio_read(cphy, 0, reg, valp);
+}
+
+static inline int simple_mdio_write(struct cphy *cphy, int reg,
+ unsigned int val)
+{
+ return mdio_write(cphy, 0, reg, val);
+}
+
+/* Convenience initializer */
+static inline void cphy_init(struct cphy *phy, adapter_t *adapter,
+ int phy_addr, struct cphy_ops *phy_ops,
+ struct mdio_ops *mdio_ops)
+{
+ phy->adapter = adapter;
+ phy->addr = phy_addr;
+ phy->ops = phy_ops;
+ if (mdio_ops) {
+ phy->mdio_read = mdio_ops->read;
+ phy->mdio_write = mdio_ops->write;
+ }
+}
+
+/* Operations of the PHY-instance factory */
+struct gphy {
+ /* Construct a PHY instance with the given PHY address */
+ struct cphy *(*create)(adapter_t *adapter, int phy_addr,
+ struct mdio_ops *mdio_ops);
+
+ /*
+ * Reset the PHY chip. This resets the whole PHY chip, not individual
+ * ports.
+ */
+ int (*reset)(adapter_t *adapter);
+};
+
+extern struct gphy t1_mv88x201x_ops;
+extern struct gphy t1_dummy_phy_ops;
+
+#endif /* _CXGB_CPHY_H_ */
diff --git a/drivers/net/chelsio/cpl5_cmd.h b/drivers/net/chelsio/cpl5_cmd.h
new file mode 100644
index 000000000000..27925e487bcf
--- /dev/null
+++ b/drivers/net/chelsio/cpl5_cmd.h
@@ -0,0 +1,145 @@
+/*****************************************************************************
+ * *
+ * File: cpl5_cmd.h *
+ * $Revision: 1.6 $ *
+ * $Date: 2005/06/21 18:29:47 $ *
+ * Description: *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#ifndef _CXGB_CPL5_CMD_H_
+#define _CXGB_CPL5_CMD_H_
+
+#include <asm/byteorder.h>
+
+#if !defined(__LITTLE_ENDIAN_BITFIELD) && !defined(__BIG_ENDIAN_BITFIELD)
+#error "Adjust your <asm/byteorder.h> defines"
+#endif
+
+enum CPL_opcode {
+ CPL_RX_PKT = 0xAD,
+ CPL_TX_PKT = 0xB2,
+ CPL_TX_PKT_LSO = 0xB6,
+};
+
+enum { /* TX_PKT_LSO ethernet types */
+ CPL_ETH_II,
+ CPL_ETH_II_VLAN,
+ CPL_ETH_802_3,
+ CPL_ETH_802_3_VLAN
+};
+
+struct cpl_rx_data {
+ u32 rsvd0;
+ u32 len;
+ u32 seq;
+ u16 urg;
+ u8 rsvd1;
+ u8 status;
+};
+
+/*
+ * We want this header's alignment to be no more stringent than 2-byte aligned.
+ * All fields are u8 or u16 except for the length. However that field is not
+ * used so we break it into 2 16-bit parts to easily meet our alignment needs.
+ */
+struct cpl_tx_pkt {
+ u8 opcode;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ u8 iff:4;
+ u8 ip_csum_dis:1;
+ u8 l4_csum_dis:1;
+ u8 vlan_valid:1;
+ u8 rsvd:1;
+#else
+ u8 rsvd:1;
+ u8 vlan_valid:1;
+ u8 l4_csum_dis:1;
+ u8 ip_csum_dis:1;
+ u8 iff:4;
+#endif
+ u16 vlan;
+ u16 len_hi;
+ u16 len_lo;
+};
+
+struct cpl_tx_pkt_lso {
+ u8 opcode;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ u8 iff:4;
+ u8 ip_csum_dis:1;
+ u8 l4_csum_dis:1;
+ u8 vlan_valid:1;
+ u8 rsvd:1;
+#else
+ u8 rsvd:1;
+ u8 vlan_valid:1;
+ u8 l4_csum_dis:1;
+ u8 ip_csum_dis:1;
+ u8 iff:4;
+#endif
+ u16 vlan;
+ u32 len;
+
+ u32 rsvd2;
+ u8 rsvd3;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ u8 tcp_hdr_words:4;
+ u8 ip_hdr_words:4;
+#else
+ u8 ip_hdr_words:4;
+ u8 tcp_hdr_words:4;
+#endif
+ u16 eth_type_mss;
+};
+
+struct cpl_rx_pkt {
+ u8 opcode;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ u8 iff:4;
+ u8 csum_valid:1;
+ u8 bad_pkt:1;
+ u8 vlan_valid:1;
+ u8 rsvd:1;
+#else
+ u8 rsvd:1;
+ u8 vlan_valid:1;
+ u8 bad_pkt:1;
+ u8 csum_valid:1;
+ u8 iff:4;
+#endif
+ u16 csum;
+ u16 vlan;
+ u16 len;
+};
+
+#endif /* _CXGB_CPL5_CMD_H_ */
diff --git a/drivers/net/chelsio/cxgb2.c b/drivers/net/chelsio/cxgb2.c
new file mode 100644
index 000000000000..28ae478b386d
--- /dev/null
+++ b/drivers/net/chelsio/cxgb2.c
@@ -0,0 +1,1256 @@
+/*****************************************************************************
+ * *
+ * File: cxgb2.c *
+ * $Revision: 1.25 $ *
+ * $Date: 2005/06/22 00:43:25 $ *
+ * Description: *
+ * Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#include "common.h"
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/mii.h>
+#include <linux/sockios.h>
+#include <linux/proc_fs.h>
+#include <linux/dma-mapping.h>
+#include <asm/uaccess.h>
+
+#include "cpl5_cmd.h"
+#include "regs.h"
+#include "gmac.h"
+#include "cphy.h"
+#include "sge.h"
+#include "espi.h"
+
+#ifdef work_struct
+#include <linux/tqueue.h>
+#define INIT_WORK INIT_TQUEUE
+#define schedule_work schedule_task
+#define flush_scheduled_work flush_scheduled_tasks
+
+static inline void schedule_mac_stats_update(struct adapter *ap, int secs)
+{
+ mod_timer(&ap->stats_update_timer, jiffies + secs * HZ);
+}
+
+static inline void cancel_mac_stats_update(struct adapter *ap)
+{
+ del_timer_sync(&ap->stats_update_timer);
+ flush_scheduled_tasks();
+}
+
+/*
+ * Stats update timer for 2.4. It schedules a task to do the actual update as
+ * we need to access MAC statistics in process context.
+ */
+static void mac_stats_timer(unsigned long data)
+{
+ struct adapter *ap = (struct adapter *)data;
+
+ schedule_task(&ap->stats_update_task);
+}
+#else
+#include <linux/workqueue.h>
+
+static inline void schedule_mac_stats_update(struct adapter *ap, int secs)
+{
+ schedule_delayed_work(&ap->stats_update_task, secs * HZ);
+}
+
+static inline void cancel_mac_stats_update(struct adapter *ap)
+{
+ cancel_delayed_work(&ap->stats_update_task);
+}
+#endif
+
+#define MAX_CMDQ_ENTRIES 16384
+#define MAX_CMDQ1_ENTRIES 1024
+#define MAX_RX_BUFFERS 16384
+#define MAX_RX_JUMBO_BUFFERS 16384
+#define MAX_TX_BUFFERS_HIGH 16384U
+#define MAX_TX_BUFFERS_LOW 1536U
+#define MIN_FL_ENTRIES 32
+
+#define PORT_MASK ((1 << MAX_NPORTS) - 1)
+
+#define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \
+ NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\
+ NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)
+
+/*
+ * The EEPROM is actually bigger but only the first few bytes are used so we
+ * only report those.
+ */
+#define EEPROM_SIZE 32
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR("Chelsio Communications");
+MODULE_LICENSE("GPL");
+
+static int dflt_msg_enable = DFLT_MSG_ENABLE;
+
+MODULE_PARM(dflt_msg_enable, "i");
+MODULE_PARM_DESC(dflt_msg_enable, "Chelsio T1 message enable bitmap");
+
+
+static const char pci_speed[][4] = {
+ "33", "66", "100", "133"
+};
+
+/*
+ * Setup MAC to receive the types of packets we want.
+ */
+static void t1_set_rxmode(struct net_device *dev)
+{
+ struct adapter *adapter = dev->priv;
+ struct cmac *mac = adapter->port[dev->if_port].mac;
+ struct t1_rx_mode rm;
+
+ rm.dev = dev;
+ rm.idx = 0;
+ rm.list = dev->mc_list;
+ mac->ops->set_rx_mode(mac, &rm);
+}
+
+static void link_report(struct port_info *p)
+{
+ if (!netif_carrier_ok(p->dev))
+ printk(KERN_INFO "%s: link down\n", p->dev->name);
+ else {
+ const char *s = "10Mbps";
+
+ switch (p->link_config.speed) {
+ case SPEED_10000: s = "10Gbps"; break;
+ case SPEED_1000: s = "1000Mbps"; break;
+ case SPEED_100: s = "100Mbps"; break;
+ }
+
+ printk(KERN_INFO "%s: link up, %s, %s-duplex\n",
+ p->dev->name, s,
+ p->link_config.duplex == DUPLEX_FULL ? "full" : "half");
+ }
+}
+
+void t1_link_changed(struct adapter *adapter, int port_id, int link_stat,
+ int speed, int duplex, int pause)
+{
+ struct port_info *p = &adapter->port[port_id];
+
+ if (link_stat != netif_carrier_ok(p->dev)) {
+ if (link_stat)
+ netif_carrier_on(p->dev);
+ else
+ netif_carrier_off(p->dev);
+ link_report(p);
+
+ }
+}
+
+static void link_start(struct port_info *p)
+{
+ struct cmac *mac = p->mac;
+
+ mac->ops->reset(mac);
+ if (mac->ops->macaddress_set)
+ mac->ops->macaddress_set(mac, p->dev->dev_addr);
+ t1_set_rxmode(p->dev);
+ t1_link_start(p->phy, mac, &p->link_config);
+ mac->ops->enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
+}
+
+static void enable_hw_csum(struct adapter *adapter)
+{
+ if (adapter->flags & TSO_CAPABLE)
+ t1_tp_set_ip_checksum_offload(adapter, 1); /* for TSO only */
+ t1_tp_set_tcp_checksum_offload(adapter, 1);
+}
+
+/*
+ * Things to do upon first use of a card.
+ * This must run with the rtnl lock held.
+ */
+static int cxgb_up(struct adapter *adapter)
+{
+ int err = 0;
+
+ if (!(adapter->flags & FULL_INIT_DONE)) {
+ err = t1_init_hw_modules(adapter);
+ if (err)
+ goto out_err;
+
+ enable_hw_csum(adapter);
+ adapter->flags |= FULL_INIT_DONE;
+ }
+
+ t1_interrupts_clear(adapter);
+ if ((err = request_irq(adapter->pdev->irq,
+ t1_select_intr_handler(adapter), SA_SHIRQ,
+ adapter->name, adapter))) {
+ goto out_err;
+ }
+ t1_sge_start(adapter->sge);
+ t1_interrupts_enable(adapter);
+ out_err:
+ return err;
+}
+
+/*
+ * Release resources when all the ports have been stopped.
+ */
+static void cxgb_down(struct adapter *adapter)
+{
+ t1_sge_stop(adapter->sge);
+ t1_interrupts_disable(adapter);
+ free_irq(adapter->pdev->irq, adapter);
+}
+
+static int cxgb_open(struct net_device *dev)
+{
+ int err;
+ struct adapter *adapter = dev->priv;
+ int other_ports = adapter->open_device_map & PORT_MASK;
+
+ if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0)
+ return err;
+
+ __set_bit(dev->if_port, &adapter->open_device_map);
+ link_start(&adapter->port[dev->if_port]);
+ netif_start_queue(dev);
+ if (!other_ports && adapter->params.stats_update_period)
+ schedule_mac_stats_update(adapter,
+ adapter->params.stats_update_period);
+ return 0;
+}
+
+static int cxgb_close(struct net_device *dev)
+{
+ struct adapter *adapter = dev->priv;
+ struct port_info *p = &adapter->port[dev->if_port];
+ struct cmac *mac = p->mac;
+
+ netif_stop_queue(dev);
+ mac->ops->disable(mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX);
+ netif_carrier_off(dev);
+
+ clear_bit(dev->if_port, &adapter->open_device_map);
+ if (adapter->params.stats_update_period &&
+ !(adapter->open_device_map & PORT_MASK)) {
+ /* Stop statistics accumulation. */
+ smp_mb__after_clear_bit();
+ spin_lock(&adapter->work_lock); /* sync with update task */
+ spin_unlock(&adapter->work_lock);
+ cancel_mac_stats_update(adapter);
+ }
+
+ if (!adapter->open_device_map)
+ cxgb_down(adapter);
+ return 0;
+}
+
+static struct net_device_stats *t1_get_stats(struct net_device *dev)
+{
+ struct adapter *adapter = dev->priv;
+ struct port_info *p = &adapter->port[dev->if_port];
+ struct net_device_stats *ns = &p->netstats;
+ const struct cmac_statistics *pstats;
+
+ /* Do a full update of the MAC stats */
+ pstats = p->mac->ops->statistics_update(p->mac,
+ MAC_STATS_UPDATE_FULL);
+
+ ns->tx_packets = pstats->TxUnicastFramesOK +
+ pstats->TxMulticastFramesOK + pstats->TxBroadcastFramesOK;
+
+ ns->rx_packets = pstats->RxUnicastFramesOK +
+ pstats->RxMulticastFramesOK + pstats->RxBroadcastFramesOK;
+
+ ns->tx_bytes = pstats->TxOctetsOK;
+ ns->rx_bytes = pstats->RxOctetsOK;
+
+ ns->tx_errors = pstats->TxLateCollisions + pstats->TxLengthErrors +
+ pstats->TxUnderrun + pstats->TxFramesAbortedDueToXSCollisions;
+ ns->rx_errors = pstats->RxDataErrors + pstats->RxJabberErrors +
+ pstats->RxFCSErrors + pstats->RxAlignErrors +
+ pstats->RxSequenceErrors + pstats->RxFrameTooLongErrors +
+ pstats->RxSymbolErrors + pstats->RxRuntErrors;
+
+ ns->multicast = pstats->RxMulticastFramesOK;
+ ns->collisions = pstats->TxTotalCollisions;
+
+ /* detailed rx_errors */
+ ns->rx_length_errors = pstats->RxFrameTooLongErrors +
+ pstats->RxJabberErrors;
+ ns->rx_over_errors = 0;
+ ns->rx_crc_errors = pstats->RxFCSErrors;
+ ns->rx_frame_errors = pstats->RxAlignErrors;
+ ns->rx_fifo_errors = 0;
+ ns->rx_missed_errors = 0;
+
+ /* detailed tx_errors */
+ ns->tx_aborted_errors = pstats->TxFramesAbortedDueToXSCollisions;
+ ns->tx_carrier_errors = 0;
+ ns->tx_fifo_errors = pstats->TxUnderrun;
+ ns->tx_heartbeat_errors = 0;
+ ns->tx_window_errors = pstats->TxLateCollisions;
+ return ns;
+}
+
+static u32 get_msglevel(struct net_device *dev)
+{
+ struct adapter *adapter = dev->priv;
+
+ return adapter->msg_enable;
+}
+
+static void set_msglevel(struct net_device *dev, u32 val)
+{
+ struct adapter *adapter = dev->priv;
+
+ adapter->msg_enable = val;
+}
+
+static char stats_strings[][ETH_GSTRING_LEN] = {
+ "TxOctetsOK",
+ "TxOctetsBad",
+ "TxUnicastFramesOK",
+ "TxMulticastFramesOK",
+ "TxBroadcastFramesOK",
+ "TxPauseFrames",
+ "TxFramesWithDeferredXmissions",
+ "TxLateCollisions",
+ "TxTotalCollisions",
+ "TxFramesAbortedDueToXSCollisions",
+ "TxUnderrun",
+ "TxLengthErrors",
+ "TxInternalMACXmitError",
+ "TxFramesWithExcessiveDeferral",
+ "TxFCSErrors",
+
+ "RxOctetsOK",
+ "RxOctetsBad",
+ "RxUnicastFramesOK",
+ "RxMulticastFramesOK",
+ "RxBroadcastFramesOK",
+ "RxPauseFrames",
+ "RxFCSErrors",
+ "RxAlignErrors",
+ "RxSymbolErrors",
+ "RxDataErrors",
+ "RxSequenceErrors",
+ "RxRuntErrors",
+ "RxJabberErrors",
+ "RxInternalMACRcvError",
+ "RxInRangeLengthErrors",
+ "RxOutOfRangeLengthField",
+ "RxFrameTooLongErrors",
+
+ "TSO",
+ "VLANextractions",
+ "VLANinsertions",
+ "RxCsumGood",
+ "TxCsumOffload",
+ "RxDrops"
+
+ "respQ_empty",
+ "respQ_overflow",
+ "freelistQ_empty",
+ "pkt_too_big",
+ "pkt_mismatch",
+ "cmdQ_full0",
+ "cmdQ_full1",
+ "tx_ipfrags",
+ "tx_reg_pkts",
+ "tx_lso_pkts",
+ "tx_do_cksum",
+
+ "espi_DIP2ParityErr",
+ "espi_DIP4Err",
+ "espi_RxDrops",
+ "espi_TxDrops",
+ "espi_RxOvfl",
+ "espi_ParityErr"
+};
+
+#define T2_REGMAP_SIZE (3 * 1024)
+
+static int get_regs_len(struct net_device *dev)
+{
+ return T2_REGMAP_SIZE;
+}
+
+static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct adapter *adapter = dev->priv;
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->fw_version, "N/A");
+ strcpy(info->bus_info, pci_name(adapter->pdev));
+}
+
+static int get_stats_count(struct net_device *dev)
+{
+ return ARRAY_SIZE(stats_strings);
+}
+
+static void get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ if (stringset == ETH_SS_STATS)
+ memcpy(data, stats_strings, sizeof(stats_strings));
+}
+
+static void get_stats(struct net_device *dev, struct ethtool_stats *stats,
+ u64 *data)
+{
+ struct adapter *adapter = dev->priv;
+ struct cmac *mac = adapter->port[dev->if_port].mac;
+ const struct cmac_statistics *s;
+ const struct sge_port_stats *ss;
+ const struct sge_intr_counts *t;
+
+ s = mac->ops->statistics_update(mac, MAC_STATS_UPDATE_FULL);
+ ss = t1_sge_get_port_stats(adapter->sge, dev->if_port);
+ t = t1_sge_get_intr_counts(adapter->sge);
+
+ *data++ = s->TxOctetsOK;
+ *data++ = s->TxOctetsBad;
+ *data++ = s->TxUnicastFramesOK;
+ *data++ = s->TxMulticastFramesOK;
+ *data++ = s->TxBroadcastFramesOK;
+ *data++ = s->TxPauseFrames;
+ *data++ = s->TxFramesWithDeferredXmissions;
+ *data++ = s->TxLateCollisions;
+ *data++ = s->TxTotalCollisions;
+ *data++ = s->TxFramesAbortedDueToXSCollisions;
+ *data++ = s->TxUnderrun;
+ *data++ = s->TxLengthErrors;
+ *data++ = s->TxInternalMACXmitError;
+ *data++ = s->TxFramesWithExcessiveDeferral;
+ *data++ = s->TxFCSErrors;
+
+ *data++ = s->RxOctetsOK;
+ *data++ = s->RxOctetsBad;
+ *data++ = s->RxUnicastFramesOK;
+ *data++ = s->RxMulticastFramesOK;
+ *data++ = s->RxBroadcastFramesOK;
+ *data++ = s->RxPauseFrames;
+ *data++ = s->RxFCSErrors;
+ *data++ = s->RxAlignErrors;
+ *data++ = s->RxSymbolErrors;
+ *data++ = s->RxDataErrors;
+ *data++ = s->RxSequenceErrors;
+ *data++ = s->RxRuntErrors;
+ *data++ = s->RxJabberErrors;
+ *data++ = s->RxInternalMACRcvError;
+ *data++ = s->RxInRangeLengthErrors;
+ *data++ = s->RxOutOfRangeLengthField;
+ *data++ = s->RxFrameTooLongErrors;
+
+ *data++ = ss->tso;
+ *data++ = ss->vlan_xtract;
+ *data++ = ss->vlan_insert;
+ *data++ = ss->rx_cso_good;
+ *data++ = ss->tx_cso;
+ *data++ = ss->rx_drops;
+
+ *data++ = (u64)t->respQ_empty;
+ *data++ = (u64)t->respQ_overflow;
+ *data++ = (u64)t->freelistQ_empty;
+ *data++ = (u64)t->pkt_too_big;
+ *data++ = (u64)t->pkt_mismatch;
+ *data++ = (u64)t->cmdQ_full[0];
+ *data++ = (u64)t->cmdQ_full[1];
+ *data++ = (u64)t->tx_ipfrags;
+ *data++ = (u64)t->tx_reg_pkts;
+ *data++ = (u64)t->tx_lso_pkts;
+ *data++ = (u64)t->tx_do_cksum;
+}
+
+static inline void reg_block_dump(struct adapter *ap, void *buf,
+ unsigned int start, unsigned int end)
+{
+ u32 *p = buf + start;
+
+ for ( ; start <= end; start += sizeof(u32))
+ *p++ = readl(ap->regs + start);
+}
+
+static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *buf)
+{
+ struct adapter *ap = dev->priv;
+
+ /*
+ * Version scheme: bits 0..9: chip version, bits 10..15: chip revision
+ */
+ regs->version = 2;
+
+ memset(buf, 0, T2_REGMAP_SIZE);
+ reg_block_dump(ap, buf, 0, A_SG_RESPACCUTIMER);
+}
+
+static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct adapter *adapter = dev->priv;
+ struct port_info *p = &adapter->port[dev->if_port];
+
+ cmd->supported = p->link_config.supported;
+ cmd->advertising = p->link_config.advertising;
+
+ if (netif_carrier_ok(dev)) {
+ cmd->speed = p->link_config.speed;
+ cmd->duplex = p->link_config.duplex;
+ } else {
+ cmd->speed = -1;
+ cmd->duplex = -1;
+ }
+
+ cmd->port = (cmd->supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE;
+ cmd->phy_address = p->phy->addr;
+ cmd->transceiver = XCVR_EXTERNAL;
+ cmd->autoneg = p->link_config.autoneg;
+ cmd->maxtxpkt = 0;
+ cmd->maxrxpkt = 0;
+ return 0;
+}
+
+static int speed_duplex_to_caps(int speed, int duplex)
+{
+ int cap = 0;
+
+ switch (speed) {
+ case SPEED_10:
+ if (duplex == DUPLEX_FULL)
+ cap = SUPPORTED_10baseT_Full;
+ else
+ cap = SUPPORTED_10baseT_Half;
+ break;
+ case SPEED_100:
+ if (duplex == DUPLEX_FULL)
+ cap = SUPPORTED_100baseT_Full;
+ else
+ cap = SUPPORTED_100baseT_Half;
+ break;
+ case SPEED_1000:
+ if (duplex == DUPLEX_FULL)
+ cap = SUPPORTED_1000baseT_Full;
+ else
+ cap = SUPPORTED_1000baseT_Half;
+ break;
+ case SPEED_10000:
+ if (duplex == DUPLEX_FULL)
+ cap = SUPPORTED_10000baseT_Full;
+ }
+ return cap;
+}
+
+#define ADVERTISED_MASK (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \
+ ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \
+ ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | \
+ ADVERTISED_10000baseT_Full)
+
+static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct adapter *adapter = dev->priv;
+ struct port_info *p = &adapter->port[dev->if_port];
+ struct link_config *lc = &p->link_config;
+
+ if (!(lc->supported & SUPPORTED_Autoneg))
+ return -EOPNOTSUPP; /* can't change speed/duplex */
+
+ if (cmd->autoneg == AUTONEG_DISABLE) {
+ int cap = speed_duplex_to_caps(cmd->speed, cmd->duplex);
+
+ if (!(lc->supported & cap) || cmd->speed == SPEED_1000)
+ return -EINVAL;
+ lc->requested_speed = cmd->speed;
+ lc->requested_duplex = cmd->duplex;
+ lc->advertising = 0;
+ } else {
+ cmd->advertising &= ADVERTISED_MASK;
+ if (cmd->advertising & (cmd->advertising - 1))
+ cmd->advertising = lc->supported;
+ cmd->advertising &= lc->supported;
+ if (!cmd->advertising)
+ return -EINVAL;
+ lc->requested_speed = SPEED_INVALID;
+ lc->requested_duplex = DUPLEX_INVALID;
+ lc->advertising = cmd->advertising | ADVERTISED_Autoneg;
+ }
+ lc->autoneg = cmd->autoneg;
+ if (netif_running(dev))
+ t1_link_start(p->phy, p->mac, lc);
+ return 0;
+}
+
+static void get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *epause)
+{
+ struct adapter *adapter = dev->priv;
+ struct port_info *p = &adapter->port[dev->if_port];
+
+ epause->autoneg = (p->link_config.requested_fc & PAUSE_AUTONEG) != 0;
+ epause->rx_pause = (p->link_config.fc & PAUSE_RX) != 0;
+ epause->tx_pause = (p->link_config.fc & PAUSE_TX) != 0;
+}
+
+static int set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *epause)
+{
+ struct adapter *adapter = dev->priv;
+ struct port_info *p = &adapter->port[dev->if_port];
+ struct link_config *lc = &p->link_config;
+
+ if (epause->autoneg == AUTONEG_DISABLE)
+ lc->requested_fc = 0;
+ else if (lc->supported & SUPPORTED_Autoneg)
+ lc->requested_fc = PAUSE_AUTONEG;
+ else
+ return -EINVAL;
+
+ if (epause->rx_pause)
+ lc->requested_fc |= PAUSE_RX;
+ if (epause->tx_pause)
+ lc->requested_fc |= PAUSE_TX;
+ if (lc->autoneg == AUTONEG_ENABLE) {
+ if (netif_running(dev))
+ t1_link_start(p->phy, p->mac, lc);
+ } else {
+ lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);
+ if (netif_running(dev))
+ p->mac->ops->set_speed_duplex_fc(p->mac, -1, -1,
+ lc->fc);
+ }
+ return 0;
+}
+
+static u32 get_rx_csum(struct net_device *dev)
+{
+ struct adapter *adapter = dev->priv;
+
+ return (adapter->flags & RX_CSUM_ENABLED) != 0;
+}
+
+static int set_rx_csum(struct net_device *dev, u32 data)
+{
+ struct adapter *adapter = dev->priv;
+
+ if (data)
+ adapter->flags |= RX_CSUM_ENABLED;
+ else
+ adapter->flags &= ~RX_CSUM_ENABLED;
+ return 0;
+}
+
+static int set_tso(struct net_device *dev, u32 value)
+{
+ struct adapter *adapter = dev->priv;
+
+ if (!(adapter->flags & TSO_CAPABLE))
+ return value ? -EOPNOTSUPP : 0;
+ return ethtool_op_set_tso(dev, value);
+}
+
+static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
+{
+ struct adapter *adapter = dev->priv;
+ int jumbo_fl = t1_is_T1B(adapter) ? 1 : 0;
+
+ e->rx_max_pending = MAX_RX_BUFFERS;
+ e->rx_mini_max_pending = 0;
+ e->rx_jumbo_max_pending = MAX_RX_JUMBO_BUFFERS;
+ e->tx_max_pending = MAX_CMDQ_ENTRIES;
+
+ e->rx_pending = adapter->params.sge.freelQ_size[!jumbo_fl];
+ e->rx_mini_pending = 0;
+ e->rx_jumbo_pending = adapter->params.sge.freelQ_size[jumbo_fl];
+ e->tx_pending = adapter->params.sge.cmdQ_size[0];
+}
+
+static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
+{
+ struct adapter *adapter = dev->priv;
+ int jumbo_fl = t1_is_T1B(adapter) ? 1 : 0;
+
+ if (e->rx_pending > MAX_RX_BUFFERS || e->rx_mini_pending ||
+ e->rx_jumbo_pending > MAX_RX_JUMBO_BUFFERS ||
+ e->tx_pending > MAX_CMDQ_ENTRIES ||
+ e->rx_pending < MIN_FL_ENTRIES ||
+ e->rx_jumbo_pending < MIN_FL_ENTRIES ||
+ e->tx_pending < (adapter->params.nports + 1) * (MAX_SKB_FRAGS + 1))
+ return -EINVAL;
+
+ if (adapter->flags & FULL_INIT_DONE)
+ return -EBUSY;
+
+ adapter->params.sge.freelQ_size[!jumbo_fl] = e->rx_pending;
+ adapter->params.sge.freelQ_size[jumbo_fl] = e->rx_jumbo_pending;
+ adapter->params.sge.cmdQ_size[0] = e->tx_pending;
+ adapter->params.sge.cmdQ_size[1] = e->tx_pending > MAX_CMDQ1_ENTRIES ?
+ MAX_CMDQ1_ENTRIES : e->tx_pending;
+ return 0;
+}
+
+static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
+{
+ struct adapter *adapter = dev->priv;
+
+ /*
+ * If RX coalescing is requested we use NAPI, otherwise interrupts.
+ * This choice can be made only when all ports and the TOE are off.
+ */
+ if (adapter->open_device_map == 0)
+ adapter->params.sge.polling = c->use_adaptive_rx_coalesce;
+
+ if (adapter->params.sge.polling) {
+ adapter->params.sge.rx_coalesce_usecs = 0;
+ } else {
+ adapter->params.sge.rx_coalesce_usecs = c->rx_coalesce_usecs;
+ }
+ adapter->params.sge.coalesce_enable = c->use_adaptive_rx_coalesce;
+ adapter->params.sge.sample_interval_usecs = c->rate_sample_interval;
+ t1_sge_set_coalesce_params(adapter->sge, &adapter->params.sge);
+ return 0;
+}
+
+static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
+{
+ struct adapter *adapter = dev->priv;
+
+ c->rx_coalesce_usecs = adapter->params.sge.rx_coalesce_usecs;
+ c->rate_sample_interval = adapter->params.sge.sample_interval_usecs;
+ c->use_adaptive_rx_coalesce = adapter->params.sge.coalesce_enable;
+ return 0;
+}
+
+static int get_eeprom_len(struct net_device *dev)
+{
+ return EEPROM_SIZE;
+}
+
+#define EEPROM_MAGIC(ap) \
+ (PCI_VENDOR_ID_CHELSIO | ((ap)->params.chip_version << 16))
+
+static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e,
+ u8 *data)
+{
+ int i;
+ u8 buf[EEPROM_SIZE] __attribute__((aligned(4)));
+ struct adapter *adapter = dev->priv;
+
+ e->magic = EEPROM_MAGIC(adapter);
+ for (i = e->offset & ~3; i < e->offset + e->len; i += sizeof(u32))
+ t1_seeprom_read(adapter, i, (u32 *)&buf[i]);
+ memcpy(data, buf + e->offset, e->len);
+ return 0;
+}
+
+static struct ethtool_ops t1_ethtool_ops = {
+ .get_settings = get_settings,
+ .set_settings = set_settings,
+ .get_drvinfo = get_drvinfo,
+ .get_msglevel = get_msglevel,
+ .set_msglevel = set_msglevel,
+ .get_ringparam = get_sge_param,
+ .set_ringparam = set_sge_param,
+ .get_coalesce = get_coalesce,
+ .set_coalesce = set_coalesce,
+ .get_eeprom_len = get_eeprom_len,
+ .get_eeprom = get_eeprom,
+ .get_pauseparam = get_pauseparam,
+ .set_pauseparam = set_pauseparam,
+ .get_rx_csum = get_rx_csum,
+ .set_rx_csum = set_rx_csum,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = ethtool_op_set_tx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = ethtool_op_set_sg,
+ .get_link = ethtool_op_get_link,
+ .get_strings = get_strings,
+ .get_stats_count = get_stats_count,
+ .get_ethtool_stats = get_stats,
+ .get_regs_len = get_regs_len,
+ .get_regs = get_regs,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = set_tso,
+};
+
+static void cxgb_proc_cleanup(struct adapter *adapter,
+ struct proc_dir_entry *dir)
+{
+ const char *name;
+ name = adapter->name;
+ remove_proc_entry(name, dir);
+}
+//#define chtoe_setup_toedev(adapter) NULL
+#define update_mtu_tab(adapter)
+#define write_smt_entry(adapter, idx)
+
+static int t1_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
+{
+ struct adapter *adapter = dev->priv;
+ struct mii_ioctl_data *data = (struct mii_ioctl_data *)&req->ifr_data;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = adapter->port[dev->if_port].phy->addr;
+ /* FALLTHRU */
+ case SIOCGMIIREG: {
+ struct cphy *phy = adapter->port[dev->if_port].phy;
+ u32 val;
+
+ if (!phy->mdio_read)
+ return -EOPNOTSUPP;
+ phy->mdio_read(adapter, data->phy_id, 0, data->reg_num & 0x1f,
+ &val);
+ data->val_out = val;
+ break;
+ }
+ case SIOCSMIIREG: {
+ struct cphy *phy = adapter->port[dev->if_port].phy;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ if (!phy->mdio_write)
+ return -EOPNOTSUPP;
+ phy->mdio_write(adapter, data->phy_id, 0, data->reg_num & 0x1f,
+ data->val_in);
+ break;
+ }
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int t1_change_mtu(struct net_device *dev, int new_mtu)
+{
+ int ret;
+ struct adapter *adapter = dev->priv;
+ struct cmac *mac = adapter->port[dev->if_port].mac;
+
+ if (!mac->ops->set_mtu)
+ return -EOPNOTSUPP;
+ if (new_mtu < 68)
+ return -EINVAL;
+ if ((ret = mac->ops->set_mtu(mac, new_mtu)))
+ return ret;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static int t1_set_mac_addr(struct net_device *dev, void *p)
+{
+ struct adapter *adapter = dev->priv;
+ struct cmac *mac = adapter->port[dev->if_port].mac;
+ struct sockaddr *addr = p;
+
+ if (!mac->ops->macaddress_set)
+ return -EOPNOTSUPP;
+
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ mac->ops->macaddress_set(mac, dev->dev_addr);
+ return 0;
+}
+
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+static void vlan_rx_register(struct net_device *dev,
+ struct vlan_group *grp)
+{
+ struct adapter *adapter = dev->priv;
+
+ spin_lock_irq(&adapter->async_lock);
+ adapter->vlan_grp = grp;
+ t1_set_vlan_accel(adapter, grp != NULL);
+ spin_unlock_irq(&adapter->async_lock);
+}
+
+static void vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+{
+ struct adapter *adapter = dev->priv;
+
+ spin_lock_irq(&adapter->async_lock);
+ if (adapter->vlan_grp)
+ adapter->vlan_grp->vlan_devices[vid] = NULL;
+ spin_unlock_irq(&adapter->async_lock);
+}
+#endif
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void t1_netpoll(struct net_device *dev)
+{
+ unsigned long flags;
+ struct adapter *adapter = dev->priv;
+
+ local_irq_save(flags);
+ t1_select_intr_handler(adapter)(adapter->pdev->irq, adapter, NULL);
+ local_irq_restore(flags);
+}
+#endif
+
+/*
+ * Periodic accumulation of MAC statistics. This is used only if the MAC
+ * does not have any other way to prevent stats counter overflow.
+ */
+static void mac_stats_task(void *data)
+{
+ int i;
+ struct adapter *adapter = data;
+
+ for_each_port(adapter, i) {
+ struct port_info *p = &adapter->port[i];
+
+ if (netif_running(p->dev))
+ p->mac->ops->statistics_update(p->mac,
+ MAC_STATS_UPDATE_FAST);
+ }
+
+ /* Schedule the next statistics update if any port is active. */
+ spin_lock(&adapter->work_lock);
+ if (adapter->open_device_map & PORT_MASK)
+ schedule_mac_stats_update(adapter,
+ adapter->params.stats_update_period);
+ spin_unlock(&adapter->work_lock);
+}
+
+/*
+ * Processes elmer0 external interrupts in process context.
+ */
+static void ext_intr_task(void *data)
+{
+ struct adapter *adapter = data;
+
+ elmer0_ext_intr_handler(adapter);
+
+ /* Now reenable external interrupts */
+ spin_lock_irq(&adapter->async_lock);
+ adapter->slow_intr_mask |= F_PL_INTR_EXT;
+ writel(F_PL_INTR_EXT, adapter->regs + A_PL_CAUSE);
+ writel(adapter->slow_intr_mask | F_PL_INTR_SGE_DATA,
+ adapter->regs + A_PL_ENABLE);
+ spin_unlock_irq(&adapter->async_lock);
+}
+
+/*
+ * Interrupt-context handler for elmer0 external interrupts.
+ */
+void t1_elmer0_ext_intr(struct adapter *adapter)
+{
+ /*
+ * Schedule a task to handle external interrupts as we require
+ * a process context. We disable EXT interrupts in the interim
+ * and let the task reenable them when it's done.
+ */
+ adapter->slow_intr_mask &= ~F_PL_INTR_EXT;
+ writel(adapter->slow_intr_mask | F_PL_INTR_SGE_DATA,
+ adapter->regs + A_PL_ENABLE);
+ schedule_work(&adapter->ext_intr_handler_task);
+}
+
+void t1_fatal_err(struct adapter *adapter)
+{
+ if (adapter->flags & FULL_INIT_DONE) {
+ t1_sge_stop(adapter->sge);
+ t1_interrupts_disable(adapter);
+ }
+ CH_ALERT("%s: encountered fatal error, operation suspended\n",
+ adapter->name);
+}
+
+static int __devinit init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ static int version_printed;
+
+ int i, err, pci_using_dac = 0;
+ unsigned long mmio_start, mmio_len;
+ const struct board_info *bi;
+ struct adapter *adapter = NULL;
+ struct port_info *pi;
+
+ if (!version_printed) {
+ printk(KERN_INFO "%s - version %s\n", DRV_DESCRIPTION,
+ DRV_VERSION);
+ ++version_printed;
+ }
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+
+ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+ CH_ERR("%s: cannot find PCI device memory base address\n",
+ pci_name(pdev));
+ err = -ENODEV;
+ goto out_disable_pdev;
+ }
+
+ if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
+ pci_using_dac = 1;
+
+ if (pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK)) {
+ CH_ERR("%s: unable to obtain 64-bit DMA for"
+ "consistent allocations\n", pci_name(pdev));
+ err = -ENODEV;
+ goto out_disable_pdev;
+ }
+
+ } else if ((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK)) != 0) {
+ CH_ERR("%s: no usable DMA configuration\n", pci_name(pdev));
+ goto out_disable_pdev;
+ }
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err) {
+ CH_ERR("%s: cannot obtain PCI resources\n", pci_name(pdev));
+ goto out_disable_pdev;
+ }
+
+ pci_set_master(pdev);
+
+ mmio_start = pci_resource_start(pdev, 0);
+ mmio_len = pci_resource_len(pdev, 0);
+ bi = t1_get_board_info(ent->driver_data);
+
+ for (i = 0; i < bi->port_number; ++i) {
+ struct net_device *netdev;
+
+ netdev = alloc_etherdev(adapter ? 0 : sizeof(*adapter));
+ if (!netdev) {
+ err = -ENOMEM;
+ goto out_free_dev;
+ }
+
+ SET_MODULE_OWNER(netdev);
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+
+ if (!adapter) {
+ adapter = netdev->priv;
+ adapter->pdev = pdev;
+ adapter->port[0].dev = netdev; /* so we don't leak it */
+
+ adapter->regs = ioremap(mmio_start, mmio_len);
+ if (!adapter->regs) {
+ CH_ERR("%s: cannot map device registers\n",
+ pci_name(pdev));
+ err = -ENOMEM;
+ goto out_free_dev;
+ }
+
+ if (t1_get_board_rev(adapter, bi, &adapter->params)) {
+ err = -ENODEV; /* Can't handle this chip rev */
+ goto out_free_dev;
+ }
+
+ adapter->name = pci_name(pdev);
+ adapter->msg_enable = dflt_msg_enable;
+ adapter->mmio_len = mmio_len;
+
+ init_MUTEX(&adapter->mib_mutex);
+ spin_lock_init(&adapter->tpi_lock);
+ spin_lock_init(&adapter->work_lock);
+ spin_lock_init(&adapter->async_lock);
+
+ INIT_WORK(&adapter->ext_intr_handler_task,
+ ext_intr_task, adapter);
+ INIT_WORK(&adapter->stats_update_task, mac_stats_task,
+ adapter);
+#ifdef work_struct
+ init_timer(&adapter->stats_update_timer);
+ adapter->stats_update_timer.function = mac_stats_timer;
+ adapter->stats_update_timer.data =
+ (unsigned long)adapter;
+#endif
+
+ pci_set_drvdata(pdev, netdev);
+ }
+
+ pi = &adapter->port[i];
+ pi->dev = netdev;
+ netif_carrier_off(netdev);
+ netdev->irq = pdev->irq;
+ netdev->if_port = i;
+ netdev->mem_start = mmio_start;
+ netdev->mem_end = mmio_start + mmio_len - 1;
+ netdev->priv = adapter;
+ netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+ netdev->features |= NETIF_F_LLTX;
+
+ adapter->flags |= RX_CSUM_ENABLED | TCP_CSUM_CAPABLE;
+ if (pci_using_dac)
+ netdev->features |= NETIF_F_HIGHDMA;
+ if (vlan_tso_capable(adapter)) {
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+ adapter->flags |= VLAN_ACCEL_CAPABLE;
+ netdev->features |=
+ NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+ netdev->vlan_rx_register = vlan_rx_register;
+ netdev->vlan_rx_kill_vid = vlan_rx_kill_vid;
+#endif
+ adapter->flags |= TSO_CAPABLE;
+ netdev->features |= NETIF_F_TSO;
+ }
+
+ netdev->open = cxgb_open;
+ netdev->stop = cxgb_close;
+ netdev->hard_start_xmit = t1_start_xmit;
+ netdev->hard_header_len += (adapter->flags & TSO_CAPABLE) ?
+ sizeof(struct cpl_tx_pkt_lso) :
+ sizeof(struct cpl_tx_pkt);
+ netdev->get_stats = t1_get_stats;
+ netdev->set_multicast_list = t1_set_rxmode;
+ netdev->do_ioctl = t1_ioctl;
+ netdev->change_mtu = t1_change_mtu;
+ netdev->set_mac_address = t1_set_mac_addr;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ netdev->poll_controller = t1_netpoll;
+#endif
+ netdev->weight = 64;
+
+ SET_ETHTOOL_OPS(netdev, &t1_ethtool_ops);
+ }
+
+ if (t1_init_sw_modules(adapter, bi) < 0) {
+ err = -ENODEV;
+ goto out_free_dev;
+ }
+
+ /*
+ * The card is now ready to go. If any errors occur during device
+ * registration we do not fail the whole card but rather proceed only
+ * with the ports we manage to register successfully. However we must
+ * register at least one net device.
+ */
+ for (i = 0; i < bi->port_number; ++i) {
+ err = register_netdev(adapter->port[i].dev);
+ if (err)
+ CH_WARN("%s: cannot register net device %s, skipping\n",
+ pci_name(pdev), adapter->port[i].dev->name);
+ else {
+ /*
+ * Change the name we use for messages to the name of
+ * the first successfully registered interface.
+ */
+ if (!adapter->registered_device_map)
+ adapter->name = adapter->port[i].dev->name;
+
+ __set_bit(i, &adapter->registered_device_map);
+ }
+ }
+ if (!adapter->registered_device_map) {
+ CH_ERR("%s: could not register any net devices\n",
+ pci_name(pdev));
+ goto out_release_adapter_res;
+ }
+
+ printk(KERN_INFO "%s: %s (rev %d), %s %dMHz/%d-bit\n", adapter->name,
+ bi->desc, adapter->params.chip_revision,
+ adapter->params.pci.is_pcix ? "PCIX" : "PCI",
+ adapter->params.pci.speed, adapter->params.pci.width);
+ return 0;
+
+ out_release_adapter_res:
+ t1_free_sw_modules(adapter);
+ out_free_dev:
+ if (adapter) {
+ if (adapter->regs) iounmap(adapter->regs);
+ for (i = bi->port_number - 1; i >= 0; --i)
+ if (adapter->port[i].dev) {
+ cxgb_proc_cleanup(adapter, proc_root_driver);
+ kfree(adapter->port[i].dev);
+ }
+ }
+ pci_release_regions(pdev);
+ out_disable_pdev:
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ return err;
+}
+
+static inline void t1_sw_reset(struct pci_dev *pdev)
+{
+ pci_write_config_dword(pdev, A_PCICFG_PM_CSR, 3);
+ pci_write_config_dword(pdev, A_PCICFG_PM_CSR, 0);
+}
+
+static void __devexit remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ if (dev) {
+ int i;
+ struct adapter *adapter = dev->priv;
+
+ for_each_port(adapter, i)
+ if (test_bit(i, &adapter->registered_device_map))
+ unregister_netdev(adapter->port[i].dev);
+
+ t1_free_sw_modules(adapter);
+ iounmap(adapter->regs);
+ while (--i >= 0)
+ if (adapter->port[i].dev) {
+ cxgb_proc_cleanup(adapter, proc_root_driver);
+ kfree(adapter->port[i].dev);
+ }
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ t1_sw_reset(pdev);
+ }
+}
+
+static struct pci_driver driver = {
+ .name = DRV_NAME,
+ .id_table = t1_pci_tbl,
+ .probe = init_one,
+ .remove = __devexit_p(remove_one),
+};
+
+static int __init t1_init_module(void)
+{
+ return pci_module_init(&driver);
+}
+
+static void __exit t1_cleanup_module(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(t1_init_module);
+module_exit(t1_cleanup_module);
diff --git a/drivers/net/chelsio/elmer0.h b/drivers/net/chelsio/elmer0.h
new file mode 100644
index 000000000000..5590cb2dac19
--- /dev/null
+++ b/drivers/net/chelsio/elmer0.h
@@ -0,0 +1,151 @@
+/*****************************************************************************
+ * *
+ * File: elmer0.h *
+ * $Revision: 1.6 $ *
+ * $Date: 2005/06/21 22:49:43 $ *
+ * Description: *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#ifndef _CXGB_ELMER0_H_
+#define _CXGB_ELMER0_H_
+
+/* ELMER0 registers */
+#define A_ELMER0_VERSION 0x100000
+#define A_ELMER0_PHY_CFG 0x100004
+#define A_ELMER0_INT_ENABLE 0x100008
+#define A_ELMER0_INT_CAUSE 0x10000c
+#define A_ELMER0_GPI_CFG 0x100010
+#define A_ELMER0_GPI_STAT 0x100014
+#define A_ELMER0_GPO 0x100018
+#define A_ELMER0_PORT0_MI1_CFG 0x400000
+
+#define S_MI1_MDI_ENABLE 0
+#define V_MI1_MDI_ENABLE(x) ((x) << S_MI1_MDI_ENABLE)
+#define F_MI1_MDI_ENABLE V_MI1_MDI_ENABLE(1U)
+
+#define S_MI1_MDI_INVERT 1
+#define V_MI1_MDI_INVERT(x) ((x) << S_MI1_MDI_INVERT)
+#define F_MI1_MDI_INVERT V_MI1_MDI_INVERT(1U)
+
+#define S_MI1_PREAMBLE_ENABLE 2
+#define V_MI1_PREAMBLE_ENABLE(x) ((x) << S_MI1_PREAMBLE_ENABLE)
+#define F_MI1_PREAMBLE_ENABLE V_MI1_PREAMBLE_ENABLE(1U)
+
+#define S_MI1_SOF 3
+#define M_MI1_SOF 0x3
+#define V_MI1_SOF(x) ((x) << S_MI1_SOF)
+#define G_MI1_SOF(x) (((x) >> S_MI1_SOF) & M_MI1_SOF)
+
+#define S_MI1_CLK_DIV 5
+#define M_MI1_CLK_DIV 0xff
+#define V_MI1_CLK_DIV(x) ((x) << S_MI1_CLK_DIV)
+#define G_MI1_CLK_DIV(x) (((x) >> S_MI1_CLK_DIV) & M_MI1_CLK_DIV)
+
+#define A_ELMER0_PORT0_MI1_ADDR 0x400004
+
+#define S_MI1_REG_ADDR 0
+#define M_MI1_REG_ADDR 0x1f
+#define V_MI1_REG_ADDR(x) ((x) << S_MI1_REG_ADDR)
+#define G_MI1_REG_ADDR(x) (((x) >> S_MI1_REG_ADDR) & M_MI1_REG_ADDR)
+
+#define S_MI1_PHY_ADDR 5
+#define M_MI1_PHY_ADDR 0x1f
+#define V_MI1_PHY_ADDR(x) ((x) << S_MI1_PHY_ADDR)
+#define G_MI1_PHY_ADDR(x) (((x) >> S_MI1_PHY_ADDR) & M_MI1_PHY_ADDR)
+
+#define A_ELMER0_PORT0_MI1_DATA 0x400008
+
+#define S_MI1_DATA 0
+#define M_MI1_DATA 0xffff
+#define V_MI1_DATA(x) ((x) << S_MI1_DATA)
+#define G_MI1_DATA(x) (((x) >> S_MI1_DATA) & M_MI1_DATA)
+
+#define A_ELMER0_PORT0_MI1_OP 0x40000c
+
+#define S_MI1_OP 0
+#define M_MI1_OP 0x3
+#define V_MI1_OP(x) ((x) << S_MI1_OP)
+#define G_MI1_OP(x) (((x) >> S_MI1_OP) & M_MI1_OP)
+
+#define S_MI1_ADDR_AUTOINC 2
+#define V_MI1_ADDR_AUTOINC(x) ((x) << S_MI1_ADDR_AUTOINC)
+#define F_MI1_ADDR_AUTOINC V_MI1_ADDR_AUTOINC(1U)
+
+#define S_MI1_OP_BUSY 31
+#define V_MI1_OP_BUSY(x) ((x) << S_MI1_OP_BUSY)
+#define F_MI1_OP_BUSY V_MI1_OP_BUSY(1U)
+
+#define A_ELMER0_PORT1_MI1_CFG 0x500000
+#define A_ELMER0_PORT1_MI1_ADDR 0x500004
+#define A_ELMER0_PORT1_MI1_DATA 0x500008
+#define A_ELMER0_PORT1_MI1_OP 0x50000c
+#define A_ELMER0_PORT2_MI1_CFG 0x600000
+#define A_ELMER0_PORT2_MI1_ADDR 0x600004
+#define A_ELMER0_PORT2_MI1_DATA 0x600008
+#define A_ELMER0_PORT2_MI1_OP 0x60000c
+#define A_ELMER0_PORT3_MI1_CFG 0x700000
+#define A_ELMER0_PORT3_MI1_ADDR 0x700004
+#define A_ELMER0_PORT3_MI1_DATA 0x700008
+#define A_ELMER0_PORT3_MI1_OP 0x70000c
+
+/* Simple bit definition for GPI and GP0 registers. */
+#define ELMER0_GP_BIT0 0x0001
+#define ELMER0_GP_BIT1 0x0002
+#define ELMER0_GP_BIT2 0x0004
+#define ELMER0_GP_BIT3 0x0008
+#define ELMER0_GP_BIT4 0x0010
+#define ELMER0_GP_BIT5 0x0020
+#define ELMER0_GP_BIT6 0x0040
+#define ELMER0_GP_BIT7 0x0080
+#define ELMER0_GP_BIT8 0x0100
+#define ELMER0_GP_BIT9 0x0200
+#define ELMER0_GP_BIT10 0x0400
+#define ELMER0_GP_BIT11 0x0800
+#define ELMER0_GP_BIT12 0x1000
+#define ELMER0_GP_BIT13 0x2000
+#define ELMER0_GP_BIT14 0x4000
+#define ELMER0_GP_BIT15 0x8000
+#define ELMER0_GP_BIT16 0x10000
+#define ELMER0_GP_BIT17 0x20000
+#define ELMER0_GP_BIT18 0x40000
+#define ELMER0_GP_BIT19 0x80000
+
+#define MI1_OP_DIRECT_WRITE 1
+#define MI1_OP_DIRECT_READ 2
+
+#define MI1_OP_INDIRECT_ADDRESS 0
+#define MI1_OP_INDIRECT_WRITE 1
+#define MI1_OP_INDIRECT_READ_INC 2
+#define MI1_OP_INDIRECT_READ 3
+
+#endif /* _CXGB_ELMER0_H_ */
diff --git a/drivers/net/chelsio/espi.c b/drivers/net/chelsio/espi.c
new file mode 100644
index 000000000000..230642571c92
--- /dev/null
+++ b/drivers/net/chelsio/espi.c
@@ -0,0 +1,346 @@
+/*****************************************************************************
+ * *
+ * File: espi.c *
+ * $Revision: 1.14 $ *
+ * $Date: 2005/05/14 00:59:32 $ *
+ * Description: *
+ * Ethernet SPI functionality. *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#include "common.h"
+#include "regs.h"
+#include "espi.h"
+
+struct peespi {
+ adapter_t *adapter;
+ struct espi_intr_counts intr_cnt;
+ u32 misc_ctrl;
+ spinlock_t lock;
+};
+
+#define ESPI_INTR_MASK (F_DIP4ERR | F_RXDROP | F_TXDROP | F_RXOVERFLOW | \
+ F_RAMPARITYERR | F_DIP2PARITYERR)
+#define MON_MASK (V_MONITORED_PORT_NUM(3) | F_MONITORED_DIRECTION \
+ | F_MONITORED_INTERFACE)
+
+#define TRICN_CNFG 14
+#define TRICN_CMD_READ 0x11
+#define TRICN_CMD_WRITE 0x21
+#define TRICN_CMD_ATTEMPTS 10
+
+static int tricn_write(adapter_t *adapter, int bundle_addr, int module_addr,
+ int ch_addr, int reg_offset, u32 wr_data)
+{
+ int busy, attempts = TRICN_CMD_ATTEMPTS;
+
+ writel(V_WRITE_DATA(wr_data) |
+ V_REGISTER_OFFSET(reg_offset) |
+ V_CHANNEL_ADDR(ch_addr) | V_MODULE_ADDR(module_addr) |
+ V_BUNDLE_ADDR(bundle_addr) |
+ V_SPI4_COMMAND(TRICN_CMD_WRITE),
+ adapter->regs + A_ESPI_CMD_ADDR);
+ writel(0, adapter->regs + A_ESPI_GOSTAT);
+
+ do {
+ busy = readl(adapter->regs + A_ESPI_GOSTAT) & F_ESPI_CMD_BUSY;
+ } while (busy && --attempts);
+
+ if (busy)
+ CH_ERR("%s: TRICN write timed out\n", adapter->name);
+
+ return busy;
+}
+
+/* 1. Deassert rx_reset_core. */
+/* 2. Program TRICN_CNFG registers. */
+/* 3. Deassert rx_reset_link */
+static int tricn_init(adapter_t *adapter)
+{
+ int i = 0;
+ int sme = 1;
+ int stat = 0;
+ int timeout = 0;
+ int is_ready = 0;
+ int dynamic_deskew = 0;
+
+ if (dynamic_deskew)
+ sme = 0;
+
+
+ /* 1 */
+ timeout=1000;
+ do {
+ stat = readl(adapter->regs + A_ESPI_RX_RESET);
+ is_ready = (stat & 0x4);
+ timeout--;
+ udelay(5);
+ } while (!is_ready || (timeout==0));
+ writel(0x2, adapter->regs + A_ESPI_RX_RESET);
+ if (timeout==0)
+ {
+ CH_ERR("ESPI : ERROR : Timeout tricn_init() \n");
+ t1_fatal_err(adapter);
+ }
+
+ /* 2 */
+ if (sme) {
+ tricn_write(adapter, 0, 0, 0, TRICN_CNFG, 0x81);
+ tricn_write(adapter, 0, 1, 0, TRICN_CNFG, 0x81);
+ tricn_write(adapter, 0, 2, 0, TRICN_CNFG, 0x81);
+ }
+ for (i=1; i<= 8; i++) tricn_write(adapter, 0, 0, i, TRICN_CNFG, 0xf1);
+ for (i=1; i<= 2; i++) tricn_write(adapter, 0, 1, i, TRICN_CNFG, 0xf1);
+ for (i=1; i<= 3; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
+ for (i=4; i<= 4; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
+ for (i=5; i<= 5; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
+ for (i=6; i<= 6; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
+ for (i=7; i<= 7; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0x80);
+ for (i=8; i<= 8; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
+
+ /* 3 */
+ writel(0x3, adapter->regs + A_ESPI_RX_RESET);
+
+ return 0;
+}
+
+void t1_espi_intr_enable(struct peespi *espi)
+{
+ u32 enable, pl_intr = readl(espi->adapter->regs + A_PL_ENABLE);
+
+ /*
+ * Cannot enable ESPI interrupts on T1B because HW asserts the
+ * interrupt incorrectly, namely the driver gets ESPI interrupts
+ * but no data is actually dropped (can verify this reading the ESPI
+ * drop registers). Also, once the ESPI interrupt is asserted it
+ * cannot be cleared (HW bug).
+ */
+ enable = t1_is_T1B(espi->adapter) ? 0 : ESPI_INTR_MASK;
+ writel(enable, espi->adapter->regs + A_ESPI_INTR_ENABLE);
+ writel(pl_intr | F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE);
+}
+
+void t1_espi_intr_clear(struct peespi *espi)
+{
+ writel(0xffffffff, espi->adapter->regs + A_ESPI_INTR_STATUS);
+ writel(F_PL_INTR_ESPI, espi->adapter->regs + A_PL_CAUSE);
+}
+
+void t1_espi_intr_disable(struct peespi *espi)
+{
+ u32 pl_intr = readl(espi->adapter->regs + A_PL_ENABLE);
+
+ writel(0, espi->adapter->regs + A_ESPI_INTR_ENABLE);
+ writel(pl_intr & ~F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE);
+}
+
+int t1_espi_intr_handler(struct peespi *espi)
+{
+ u32 cnt;
+ u32 status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS);
+
+ if (status & F_DIP4ERR)
+ espi->intr_cnt.DIP4_err++;
+ if (status & F_RXDROP)
+ espi->intr_cnt.rx_drops++;
+ if (status & F_TXDROP)
+ espi->intr_cnt.tx_drops++;
+ if (status & F_RXOVERFLOW)
+ espi->intr_cnt.rx_ovflw++;
+ if (status & F_RAMPARITYERR)
+ espi->intr_cnt.parity_err++;
+ if (status & F_DIP2PARITYERR) {
+ espi->intr_cnt.DIP2_parity_err++;
+
+ /*
+ * Must read the error count to clear the interrupt
+ * that it causes.
+ */
+ cnt = readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT);
+ }
+
+ /*
+ * For T1B we need to write 1 to clear ESPI interrupts. For T2+ we
+ * write the status as is.
+ */
+ if (status && t1_is_T1B(espi->adapter))
+ status = 1;
+ writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS);
+ return 0;
+}
+
+const struct espi_intr_counts *t1_espi_get_intr_counts(struct peespi *espi)
+{
+ return &espi->intr_cnt;
+}
+
+static void espi_setup_for_pm3393(adapter_t *adapter)
+{
+ u32 wmark = t1_is_T1B(adapter) ? 0x4000 : 0x3200;
+
+ writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN0);
+ writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN1);
+ writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN2);
+ writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN3);
+ writel(0x100, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK);
+ writel(wmark, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK);
+ writel(3, adapter->regs + A_ESPI_CALENDAR_LENGTH);
+ writel(0x08000008, adapter->regs + A_ESPI_TRAIN);
+ writel(V_RX_NPORTS(1) | V_TX_NPORTS(1), adapter->regs + A_PORT_CONFIG);
+}
+
+/* T2 Init part -- */
+/* 1. Set T_ESPI_MISCCTRL_ADDR */
+/* 2. Init ESPI registers. */
+/* 3. Init TriCN Hard Macro */
+int t1_espi_init(struct peespi *espi, int mac_type, int nports)
+{
+ u32 cnt;
+
+ u32 status_enable_extra = 0;
+ adapter_t *adapter = espi->adapter;
+ u32 status, burstval = 0x800100;
+
+ /* Disable ESPI training. MACs that can handle it enable it below. */
+ writel(0, adapter->regs + A_ESPI_TRAIN);
+
+ if (is_T2(adapter)) {
+ writel(V_OUT_OF_SYNC_COUNT(4) |
+ V_DIP2_PARITY_ERR_THRES(3) |
+ V_DIP4_THRES(1), adapter->regs + A_ESPI_MISC_CONTROL);
+ if (nports == 4) {
+ /* T204: maxburst1 = 0x40, maxburst2 = 0x20 */
+ burstval = 0x200040;
+ }
+ }
+ writel(burstval, adapter->regs + A_ESPI_MAXBURST1_MAXBURST2);
+
+ switch (mac_type) {
+ case CHBT_MAC_PM3393:
+ espi_setup_for_pm3393(adapter);
+ break;
+ default:
+ return -1;
+ }
+
+ /*
+ * Make sure any pending interrupts from the SPI are
+ * Cleared before enabling the interrupt.
+ */
+ writel(ESPI_INTR_MASK, espi->adapter->regs + A_ESPI_INTR_ENABLE);
+ status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS);
+ if (status & F_DIP2PARITYERR) {
+ cnt = readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT);
+ }
+
+ /*
+ * For T1B we need to write 1 to clear ESPI interrupts. For T2+ we
+ * write the status as is.
+ */
+ if (status && t1_is_T1B(espi->adapter))
+ status = 1;
+ writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS);
+
+ writel(status_enable_extra | F_RXSTATUSENABLE,
+ adapter->regs + A_ESPI_FIFO_STATUS_ENABLE);
+
+ if (is_T2(adapter)) {
+ tricn_init(adapter);
+ /*
+ * Always position the control at the 1st port egress IN
+ * (sop,eop) counter to reduce PIOs for T/N210 workaround.
+ */
+ espi->misc_ctrl = (readl(adapter->regs + A_ESPI_MISC_CONTROL)
+ & ~MON_MASK) | (F_MONITORED_DIRECTION
+ | F_MONITORED_INTERFACE);
+ writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
+ spin_lock_init(&espi->lock);
+ }
+
+ return 0;
+}
+
+void t1_espi_destroy(struct peespi *espi)
+{
+ kfree(espi);
+}
+
+struct peespi *t1_espi_create(adapter_t *adapter)
+{
+ struct peespi *espi = kmalloc(sizeof(*espi), GFP_KERNEL);
+
+ memset(espi, 0, sizeof(*espi));
+
+ if (espi)
+ espi->adapter = adapter;
+ return espi;
+}
+
+void t1_espi_set_misc_ctrl(adapter_t *adapter, u32 val)
+{
+ struct peespi *espi = adapter->espi;
+
+ if (!is_T2(adapter))
+ return;
+ spin_lock(&espi->lock);
+ espi->misc_ctrl = (val & ~MON_MASK) |
+ (espi->misc_ctrl & MON_MASK);
+ writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
+ spin_unlock(&espi->lock);
+}
+
+u32 t1_espi_get_mon(adapter_t *adapter, u32 addr, u8 wait)
+{
+ u32 sel;
+
+ struct peespi *espi = adapter->espi;
+
+ if (!is_T2(adapter))
+ return 0;
+ sel = V_MONITORED_PORT_NUM((addr & 0x3c) >> 2);
+ if (!wait) {
+ if (!spin_trylock(&espi->lock))
+ return 0;
+ }
+ else
+ spin_lock(&espi->lock);
+ if ((sel != (espi->misc_ctrl & MON_MASK))) {
+ writel(((espi->misc_ctrl & ~MON_MASK) | sel),
+ adapter->regs + A_ESPI_MISC_CONTROL);
+ sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3);
+ writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
+ }
+ else
+ sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3);
+ spin_unlock(&espi->lock);
+ return sel;
+}
diff --git a/drivers/net/chelsio/espi.h b/drivers/net/chelsio/espi.h
new file mode 100644
index 000000000000..c90e37f8457c
--- /dev/null
+++ b/drivers/net/chelsio/espi.h
@@ -0,0 +1,68 @@
+/*****************************************************************************
+ * *
+ * File: espi.h *
+ * $Revision: 1.7 $ *
+ * $Date: 2005/06/21 18:29:47 $ *
+ * Description: *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#ifndef _CXGB_ESPI_H_
+#define _CXGB_ESPI_H_
+
+#include "common.h"
+
+struct espi_intr_counts {
+ unsigned int DIP4_err;
+ unsigned int rx_drops;
+ unsigned int tx_drops;
+ unsigned int rx_ovflw;
+ unsigned int parity_err;
+ unsigned int DIP2_parity_err;
+};
+
+struct peespi;
+
+struct peespi *t1_espi_create(adapter_t *adapter);
+void t1_espi_destroy(struct peespi *espi);
+int t1_espi_init(struct peespi *espi, int mac_type, int nports);
+
+void t1_espi_intr_enable(struct peespi *);
+void t1_espi_intr_clear(struct peespi *);
+void t1_espi_intr_disable(struct peespi *);
+int t1_espi_intr_handler(struct peespi *);
+const struct espi_intr_counts *t1_espi_get_intr_counts(struct peespi *espi);
+
+void t1_espi_set_misc_ctrl(adapter_t *adapter, u32 val);
+u32 t1_espi_get_mon(adapter_t *adapter, u32 addr, u8 wait);
+
+#endif /* _CXGB_ESPI_H_ */
diff --git a/drivers/net/chelsio/gmac.h b/drivers/net/chelsio/gmac.h
new file mode 100644
index 000000000000..746b0eeea964
--- /dev/null
+++ b/drivers/net/chelsio/gmac.h
@@ -0,0 +1,134 @@
+/*****************************************************************************
+ * *
+ * File: gmac.h *
+ * $Revision: 1.6 $ *
+ * $Date: 2005/06/21 18:29:47 $ *
+ * Description: *
+ * Generic MAC functionality. *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#ifndef _CXGB_GMAC_H_
+#define _CXGB_GMAC_H_
+
+#include "common.h"
+
+enum { MAC_STATS_UPDATE_FAST, MAC_STATS_UPDATE_FULL };
+enum { MAC_DIRECTION_RX = 1, MAC_DIRECTION_TX = 2 };
+
+struct cmac_statistics {
+ /* Transmit */
+ u64 TxOctetsOK;
+ u64 TxOctetsBad;
+ u64 TxUnicastFramesOK;
+ u64 TxMulticastFramesOK;
+ u64 TxBroadcastFramesOK;
+ u64 TxPauseFrames;
+ u64 TxFramesWithDeferredXmissions;
+ u64 TxLateCollisions;
+ u64 TxTotalCollisions;
+ u64 TxFramesAbortedDueToXSCollisions;
+ u64 TxUnderrun;
+ u64 TxLengthErrors;
+ u64 TxInternalMACXmitError;
+ u64 TxFramesWithExcessiveDeferral;
+ u64 TxFCSErrors;
+
+ /* Receive */
+ u64 RxOctetsOK;
+ u64 RxOctetsBad;
+ u64 RxUnicastFramesOK;
+ u64 RxMulticastFramesOK;
+ u64 RxBroadcastFramesOK;
+ u64 RxPauseFrames;
+ u64 RxFCSErrors;
+ u64 RxAlignErrors;
+ u64 RxSymbolErrors;
+ u64 RxDataErrors;
+ u64 RxSequenceErrors;
+ u64 RxRuntErrors;
+ u64 RxJabberErrors;
+ u64 RxInternalMACRcvError;
+ u64 RxInRangeLengthErrors;
+ u64 RxOutOfRangeLengthField;
+ u64 RxFrameTooLongErrors;
+};
+
+struct cmac_ops {
+ void (*destroy)(struct cmac *);
+ int (*reset)(struct cmac *);
+ int (*interrupt_enable)(struct cmac *);
+ int (*interrupt_disable)(struct cmac *);
+ int (*interrupt_clear)(struct cmac *);
+ int (*interrupt_handler)(struct cmac *);
+
+ int (*enable)(struct cmac *, int);
+ int (*disable)(struct cmac *, int);
+
+ int (*loopback_enable)(struct cmac *);
+ int (*loopback_disable)(struct cmac *);
+
+ int (*set_mtu)(struct cmac *, int mtu);
+ int (*set_rx_mode)(struct cmac *, struct t1_rx_mode *rm);
+
+ int (*set_speed_duplex_fc)(struct cmac *, int speed, int duplex, int fc);
+ int (*get_speed_duplex_fc)(struct cmac *, int *speed, int *duplex,
+ int *fc);
+
+ const struct cmac_statistics *(*statistics_update)(struct cmac *, int);
+
+ int (*macaddress_get)(struct cmac *, u8 mac_addr[6]);
+ int (*macaddress_set)(struct cmac *, u8 mac_addr[6]);
+};
+
+typedef struct _cmac_instance cmac_instance;
+
+struct cmac {
+ struct cmac_statistics stats;
+ adapter_t *adapter;
+ struct cmac_ops *ops;
+ cmac_instance *instance;
+};
+
+struct gmac {
+ unsigned int stats_update_period;
+ struct cmac *(*create)(adapter_t *adapter, int index);
+ int (*reset)(adapter_t *);
+};
+
+extern struct gmac t1_pm3393_ops;
+extern struct gmac t1_chelsio_mac_ops;
+extern struct gmac t1_vsc7321_ops;
+extern struct gmac t1_ixf1010_ops;
+extern struct gmac t1_dummy_mac_ops;
+
+#endif /* _CXGB_GMAC_H_ */
diff --git a/drivers/net/chelsio/mv88x201x.c b/drivers/net/chelsio/mv88x201x.c
new file mode 100644
index 000000000000..db5034282782
--- /dev/null
+++ b/drivers/net/chelsio/mv88x201x.c
@@ -0,0 +1,252 @@
+/*****************************************************************************
+ * *
+ * File: mv88x201x.c *
+ * $Revision: 1.12 $ *
+ * $Date: 2005/04/15 19:27:14 $ *
+ * Description: *
+ * Marvell PHY (mv88x201x) functionality. *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#include "cphy.h"
+#include "elmer0.h"
+
+/*
+ * The 88x2010 Rev C. requires some link status registers * to be read
+ * twice in order to get the right values. Future * revisions will fix
+ * this problem and then this macro * can disappear.
+ */
+#define MV88x2010_LINK_STATUS_BUGS 1
+
+static int led_init(struct cphy *cphy)
+{
+ /* Setup the LED registers so we can turn on/off.
+ * Writing these bits maps control to another
+ * register. mmd(0x1) addr(0x7)
+ */
+ mdio_write(cphy, 0x3, 0x8304, 0xdddd);
+ return 0;
+}
+
+static int led_link(struct cphy *cphy, u32 do_enable)
+{
+ u32 led = 0;
+#define LINK_ENABLE_BIT 0x1
+
+ mdio_read(cphy, 0x1, 0x7, &led);
+
+ if (do_enable & LINK_ENABLE_BIT) {
+ led |= LINK_ENABLE_BIT;
+ mdio_write(cphy, 0x1, 0x7, led);
+ } else {
+ led &= ~LINK_ENABLE_BIT;
+ mdio_write(cphy, 0x1, 0x7, led);
+ }
+ return 0;
+}
+
+/* Port Reset */
+static int mv88x201x_reset(struct cphy *cphy, int wait)
+{
+ /* This can be done through registers. It is not required since
+ * a full chip reset is used.
+ */
+ return 0;
+}
+
+static int mv88x201x_interrupt_enable(struct cphy *cphy)
+{
+ u32 elmer;
+
+ /* Enable PHY LASI interrupts. */
+ mdio_write(cphy, 0x1, 0x9002, 0x1);
+
+ /* Enable Marvell interrupts through Elmer0. */
+ t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
+ elmer |= ELMER0_GP_BIT6;
+ t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
+ return 0;
+}
+
+static int mv88x201x_interrupt_disable(struct cphy *cphy)
+{
+ u32 elmer;
+
+ /* Disable PHY LASI interrupts. */
+ mdio_write(cphy, 0x1, 0x9002, 0x0);
+
+ /* Disable Marvell interrupts through Elmer0. */
+ t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
+ elmer &= ~ELMER0_GP_BIT6;
+ t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
+ return 0;
+}
+
+static int mv88x201x_interrupt_clear(struct cphy *cphy)
+{
+ u32 elmer;
+ u32 val;
+
+#ifdef MV88x2010_LINK_STATUS_BUGS
+ /* Required to read twice before clear takes affect. */
+ mdio_read(cphy, 0x1, 0x9003, &val);
+ mdio_read(cphy, 0x1, 0x9004, &val);
+ mdio_read(cphy, 0x1, 0x9005, &val);
+
+ /* Read this register after the others above it else
+ * the register doesn't clear correctly.
+ */
+ mdio_read(cphy, 0x1, 0x1, &val);
+#endif
+
+ /* Clear link status. */
+ mdio_read(cphy, 0x1, 0x1, &val);
+ /* Clear PHY LASI interrupts. */
+ mdio_read(cphy, 0x1, 0x9005, &val);
+
+#ifdef MV88x2010_LINK_STATUS_BUGS
+ /* Do it again. */
+ mdio_read(cphy, 0x1, 0x9003, &val);
+ mdio_read(cphy, 0x1, 0x9004, &val);
+#endif
+
+ /* Clear Marvell interrupts through Elmer0. */
+ t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
+ elmer |= ELMER0_GP_BIT6;
+ t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
+ return 0;
+}
+
+static int mv88x201x_interrupt_handler(struct cphy *cphy)
+{
+ /* Clear interrupts */
+ mv88x201x_interrupt_clear(cphy);
+
+ /* We have only enabled link change interrupts and so
+ * cphy_cause must be a link change interrupt.
+ */
+ return cphy_cause_link_change;
+}
+
+static int mv88x201x_set_loopback(struct cphy *cphy, int on)
+{
+ return 0;
+}
+
+static int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok,
+ int *speed, int *duplex, int *fc)
+{
+ u32 val = 0;
+#define LINK_STATUS_BIT 0x4
+
+ if (link_ok) {
+ /* Read link status. */
+ mdio_read(cphy, 0x1, 0x1, &val);
+ val &= LINK_STATUS_BIT;
+ *link_ok = (val == LINK_STATUS_BIT);
+ /* Turn on/off Link LED */
+ led_link(cphy, *link_ok);
+ }
+ if (speed)
+ *speed = SPEED_10000;
+ if (duplex)
+ *duplex = DUPLEX_FULL;
+ if (fc)
+ *fc = PAUSE_RX | PAUSE_TX;
+ return 0;
+}
+
+static void mv88x201x_destroy(struct cphy *cphy)
+{
+ kfree(cphy);
+}
+
+static struct cphy_ops mv88x201x_ops = {
+ .destroy = mv88x201x_destroy,
+ .reset = mv88x201x_reset,
+ .interrupt_enable = mv88x201x_interrupt_enable,
+ .interrupt_disable = mv88x201x_interrupt_disable,
+ .interrupt_clear = mv88x201x_interrupt_clear,
+ .interrupt_handler = mv88x201x_interrupt_handler,
+ .get_link_status = mv88x201x_get_link_status,
+ .set_loopback = mv88x201x_set_loopback,
+};
+
+static struct cphy *mv88x201x_phy_create(adapter_t *adapter, int phy_addr,
+ struct mdio_ops *mdio_ops)
+{
+ u32 val;
+ struct cphy *cphy = kmalloc(sizeof(*cphy), GFP_KERNEL);
+
+ if (!cphy)
+ return NULL;
+ memset(cphy, 0, sizeof(*cphy));
+ cphy_init(cphy, adapter, phy_addr, &mv88x201x_ops, mdio_ops);
+
+ /* Commands the PHY to enable XFP's clock. */
+ mdio_read(cphy, 0x3, 0x8300, &val);
+ mdio_write(cphy, 0x3, 0x8300, val | 1);
+
+ /* Clear link status. Required because of a bug in the PHY. */
+ mdio_read(cphy, 0x1, 0x8, &val);
+ mdio_read(cphy, 0x3, 0x8, &val);
+
+ /* Allows for Link,Ack LED turn on/off */
+ led_init(cphy);
+ return cphy;
+}
+
+/* Chip Reset */
+static int mv88x201x_phy_reset(adapter_t *adapter)
+{
+ u32 val;
+
+ t1_tpi_read(adapter, A_ELMER0_GPO, &val);
+ val &= ~4;
+ t1_tpi_write(adapter, A_ELMER0_GPO, val);
+ msleep(100);
+
+ t1_tpi_write(adapter, A_ELMER0_GPO, val | 4);
+ msleep(1000);
+
+ /* Now lets enable the Laser. Delay 100us */
+ t1_tpi_read(adapter, A_ELMER0_GPO, &val);
+ val |= 0x8000;
+ t1_tpi_write(adapter, A_ELMER0_GPO, val);
+ udelay(100);
+ return 0;
+}
+
+struct gphy t1_mv88x201x_ops = {
+ mv88x201x_phy_create,
+ mv88x201x_phy_reset
+};
diff --git a/drivers/net/chelsio/pm3393.c b/drivers/net/chelsio/pm3393.c
new file mode 100644
index 000000000000..04a1404fc65e
--- /dev/null
+++ b/drivers/net/chelsio/pm3393.c
@@ -0,0 +1,826 @@
+/*****************************************************************************
+ * *
+ * File: pm3393.c *
+ * $Revision: 1.16 $ *
+ * $Date: 2005/05/14 00:59:32 $ *
+ * Description: *
+ * PMC/SIERRA (pm3393) MAC-PHY functionality. *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#include "common.h"
+#include "regs.h"
+#include "gmac.h"
+#include "elmer0.h"
+#include "suni1x10gexp_regs.h"
+
+/* 802.3ae 10Gb/s MDIO Manageable Device(MMD)
+ */
+enum {
+ MMD_RESERVED,
+ MMD_PMAPMD,
+ MMD_WIS,
+ MMD_PCS,
+ MMD_PHY_XGXS, /* XGMII Extender Sublayer */
+ MMD_DTE_XGXS,
+};
+
+enum {
+ PHY_XGXS_CTRL_1,
+ PHY_XGXS_STATUS_1
+};
+
+#define OFFSET(REG_ADDR) (REG_ADDR << 2)
+
+/* Max frame size PM3393 can handle. Includes Ethernet header and CRC. */
+#define MAX_FRAME_SIZE 9600
+
+#define IPG 12
+#define TXXG_CONF1_VAL ((IPG << SUNI1x10GEXP_BITOFF_TXXG_IPGT) | \
+ SUNI1x10GEXP_BITMSK_TXXG_32BIT_ALIGN | SUNI1x10GEXP_BITMSK_TXXG_CRCEN | \
+ SUNI1x10GEXP_BITMSK_TXXG_PADEN)
+#define RXXG_CONF1_VAL (SUNI1x10GEXP_BITMSK_RXXG_PUREP | 0x14 | \
+ SUNI1x10GEXP_BITMSK_RXXG_FLCHK | SUNI1x10GEXP_BITMSK_RXXG_CRC_STRIP)
+
+/* Update statistics every 15 minutes */
+#define STATS_TICK_SECS (15 * 60)
+
+enum { /* RMON registers */
+ RxOctetsReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_1_LOW,
+ RxUnicastFramesReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_4_LOW,
+ RxMulticastFramesReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_5_LOW,
+ RxBroadcastFramesReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_6_LOW,
+ RxPAUSEMACCtrlFramesReceived = SUNI1x10GEXP_REG_MSTAT_COUNTER_8_LOW,
+ RxFrameCheckSequenceErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_10_LOW,
+ RxFramesLostDueToInternalMACErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_11_LOW,
+ RxSymbolErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_12_LOW,
+ RxInRangeLengthErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_13_LOW,
+ RxFramesTooLongErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_15_LOW,
+ RxJabbers = SUNI1x10GEXP_REG_MSTAT_COUNTER_16_LOW,
+ RxFragments = SUNI1x10GEXP_REG_MSTAT_COUNTER_17_LOW,
+ RxUndersizedFrames = SUNI1x10GEXP_REG_MSTAT_COUNTER_18_LOW,
+
+ TxOctetsTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_33_LOW,
+ TxFramesLostDueToInternalMACTransmissionError = SUNI1x10GEXP_REG_MSTAT_COUNTER_35_LOW,
+ TxTransmitSystemError = SUNI1x10GEXP_REG_MSTAT_COUNTER_36_LOW,
+ TxUnicastFramesTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_38_LOW,
+ TxMulticastFramesTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_40_LOW,
+ TxBroadcastFramesTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_42_LOW,
+ TxPAUSEMACCtrlFramesTransmitted = SUNI1x10GEXP_REG_MSTAT_COUNTER_43_LOW
+};
+
+struct _cmac_instance {
+ u8 enabled;
+ u8 fc;
+ u8 mac_addr[6];
+};
+
+static int pmread(struct cmac *cmac, u32 reg, u32 * data32)
+{
+ t1_tpi_read(cmac->adapter, OFFSET(reg), data32);
+ return 0;
+}
+
+static int pmwrite(struct cmac *cmac, u32 reg, u32 data32)
+{
+ t1_tpi_write(cmac->adapter, OFFSET(reg), data32);
+ return 0;
+}
+
+/* Port reset. */
+static int pm3393_reset(struct cmac *cmac)
+{
+ return 0;
+}
+
+/*
+ * Enable interrupts for the PM3393
+
+ 1. Enable PM3393 BLOCK interrupts.
+ 2. Enable PM3393 Master Interrupt bit(INTE)
+ 3. Enable ELMER's PM3393 bit.
+ 4. Enable Terminator external interrupt.
+*/
+static int pm3393_interrupt_enable(struct cmac *cmac)
+{
+ u32 pl_intr;
+
+ /* PM3393 - Enabling all hardware block interrupts.
+ */
+ pmwrite(cmac, SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_ENABLE, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_XRF_INTERRUPT_ENABLE, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_ENABLE, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXOAM_INTERRUPT_ENABLE, 0xffff);
+
+ /* Don't interrupt on statistics overflow, we are polling */
+ pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_0, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_1, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_2, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_3, 0);
+
+ pmwrite(cmac, SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_ENABLE, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_PL4ODP_INTERRUPT_MASK, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_XTEF_INTERRUPT_ENABLE, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_TXOAM_INTERRUPT_ENABLE, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_3, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_MASK, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_3, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_PL4IDU_INTERRUPT_MASK, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_ENABLE, 0xffff);
+
+ /* PM3393 - Global interrupt enable
+ */
+ /* TBD XXX Disable for now until we figure out why error interrupts keep asserting. */
+ pmwrite(cmac, SUNI1x10GEXP_REG_GLOBAL_INTERRUPT_ENABLE,
+ 0 /*SUNI1x10GEXP_BITMSK_TOP_INTE */ );
+
+ /* TERMINATOR - PL_INTERUPTS_EXT */
+ pl_intr = readl(cmac->adapter->regs + A_PL_ENABLE);
+ pl_intr |= F_PL_INTR_EXT;
+ writel(pl_intr, cmac->adapter->regs + A_PL_ENABLE);
+ return 0;
+}
+
+static int pm3393_interrupt_disable(struct cmac *cmac)
+{
+ u32 elmer;
+
+ /* PM3393 - Enabling HW interrupt blocks. */
+ pmwrite(cmac, SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_ENABLE, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_XRF_INTERRUPT_ENABLE, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_ENABLE, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXOAM_INTERRUPT_ENABLE, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_0, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_1, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_2, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_3, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_ENABLE, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_PL4ODP_INTERRUPT_MASK, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_XTEF_INTERRUPT_ENABLE, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_TXOAM_INTERRUPT_ENABLE, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_3, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_MASK, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_3, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_PL4IDU_INTERRUPT_MASK, 0);
+ pmwrite(cmac, SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_ENABLE, 0);
+
+ /* PM3393 - Global interrupt enable */
+ pmwrite(cmac, SUNI1x10GEXP_REG_GLOBAL_INTERRUPT_ENABLE, 0);
+
+ /* ELMER - External chip interrupts. */
+ t1_tpi_read(cmac->adapter, A_ELMER0_INT_ENABLE, &elmer);
+ elmer &= ~ELMER0_GP_BIT1;
+ t1_tpi_write(cmac->adapter, A_ELMER0_INT_ENABLE, elmer);
+
+ /* TERMINATOR - PL_INTERUPTS_EXT */
+ /* DO NOT DISABLE TERMINATOR's EXTERNAL INTERRUPTS. ANOTHER CHIP
+ * COULD WANT THEM ENABLED. We disable PM3393 at the ELMER level.
+ */
+
+ return 0;
+}
+
+static int pm3393_interrupt_clear(struct cmac *cmac)
+{
+ u32 elmer;
+ u32 pl_intr;
+ u32 val32;
+
+ /* PM3393 - Clearing HW interrupt blocks. Note, this assumes
+ * bit WCIMODE=0 for a clear-on-read.
+ */
+ pmread(cmac, SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_STATUS, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_XRF_INTERRUPT_STATUS, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_STATUS, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_RXOAM_INTERRUPT_STATUS, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_PL4ODP_INTERRUPT, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_XTEF_INTERRUPT_STATUS, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_INTERRUPT, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_TXOAM_INTERRUPT_STATUS, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_RXXG_INTERRUPT, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_TXXG_INTERRUPT, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_PL4IDU_INTERRUPT, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_INDICATION,
+ &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_STATUS, &val32);
+ pmread(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_CHANGE, &val32);
+
+ /* PM3393 - Global interrupt status
+ */
+ pmread(cmac, SUNI1x10GEXP_REG_MASTER_INTERRUPT_STATUS, &val32);
+
+ /* ELMER - External chip interrupts.
+ */
+ t1_tpi_read(cmac->adapter, A_ELMER0_INT_CAUSE, &elmer);
+ elmer |= ELMER0_GP_BIT1;
+ t1_tpi_write(cmac->adapter, A_ELMER0_INT_CAUSE, elmer);
+
+ /* TERMINATOR - PL_INTERUPTS_EXT
+ */
+ pl_intr = readl(cmac->adapter->regs + A_PL_CAUSE);
+ pl_intr |= F_PL_INTR_EXT;
+ writel(pl_intr, cmac->adapter->regs + A_PL_CAUSE);
+
+ return 0;
+}
+
+/* Interrupt handler */
+static int pm3393_interrupt_handler(struct cmac *cmac)
+{
+ u32 master_intr_status;
+/*
+ 1. Read master interrupt register.
+ 2. Read BLOCK's interrupt status registers.
+ 3. Handle BLOCK interrupts.
+*/
+ /* Read the master interrupt status register. */
+ pmread(cmac, SUNI1x10GEXP_REG_MASTER_INTERRUPT_STATUS,
+ &master_intr_status);
+
+ /* TBD XXX Lets just clear everything for now */
+ pm3393_interrupt_clear(cmac);
+
+ return 0;
+}
+
+static int pm3393_enable(struct cmac *cmac, int which)
+{
+ if (which & MAC_DIRECTION_RX)
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_1,
+ (RXXG_CONF1_VAL | SUNI1x10GEXP_BITMSK_RXXG_RXEN));
+
+ if (which & MAC_DIRECTION_TX) {
+ u32 val = TXXG_CONF1_VAL | SUNI1x10GEXP_BITMSK_TXXG_TXEN0;
+
+ if (cmac->instance->fc & PAUSE_RX)
+ val |= SUNI1x10GEXP_BITMSK_TXXG_FCRX;
+ if (cmac->instance->fc & PAUSE_TX)
+ val |= SUNI1x10GEXP_BITMSK_TXXG_FCTX;
+ pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_1, val);
+ }
+
+ cmac->instance->enabled |= which;
+ return 0;
+}
+
+static int pm3393_enable_port(struct cmac *cmac, int which)
+{
+ /* Clear port statistics */
+ pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_CONTROL,
+ SUNI1x10GEXP_BITMSK_MSTAT_CLEAR);
+ udelay(2);
+ memset(&cmac->stats, 0, sizeof(struct cmac_statistics));
+
+ pm3393_enable(cmac, which);
+
+ /*
+ * XXX This should be done by the PHY and preferrably not at all.
+ * The PHY doesn't give us link status indication on its own so have
+ * the link management code query it instead.
+ */
+ {
+ extern void link_changed(adapter_t *adapter, int port_id);
+
+ link_changed(cmac->adapter, 0);
+ }
+ return 0;
+}
+
+static int pm3393_disable(struct cmac *cmac, int which)
+{
+ if (which & MAC_DIRECTION_RX)
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_1, RXXG_CONF1_VAL);
+ if (which & MAC_DIRECTION_TX)
+ pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_1, TXXG_CONF1_VAL);
+
+ /*
+ * The disable is graceful. Give the PM3393 time. Can't wait very
+ * long here, we may be holding locks.
+ */
+ udelay(20);
+
+ cmac->instance->enabled &= ~which;
+ return 0;
+}
+
+static int pm3393_loopback_enable(struct cmac *cmac)
+{
+ return 0;
+}
+
+static int pm3393_loopback_disable(struct cmac *cmac)
+{
+ return 0;
+}
+
+static int pm3393_set_mtu(struct cmac *cmac, int mtu)
+{
+ int enabled = cmac->instance->enabled;
+
+ /* MAX_FRAME_SIZE includes header + FCS, mtu doesn't */
+ mtu += 14 + 4;
+ if (mtu > MAX_FRAME_SIZE)
+ return -EINVAL;
+
+ /* Disable Rx/Tx MAC before configuring it. */
+ if (enabled)
+ pm3393_disable(cmac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
+
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MAX_FRAME_LENGTH, mtu);
+ pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_MAX_FRAME_SIZE, mtu);
+
+ if (enabled)
+ pm3393_enable(cmac, enabled);
+ return 0;
+}
+
+static u32 calc_crc(u8 *b, int len)
+{
+ int i;
+ u32 crc = (u32)~0;
+
+ /* calculate crc one bit at a time */
+ while (len--) {
+ crc ^= *b++;
+ for (i = 0; i < 8; i++) {
+ if (crc & 0x1)
+ crc = (crc >> 1) ^ 0xedb88320;
+ else
+ crc = (crc >> 1);
+ }
+ }
+
+ /* reverse bits */
+ crc = ((crc >> 4) & 0x0f0f0f0f) | ((crc << 4) & 0xf0f0f0f0);
+ crc = ((crc >> 2) & 0x33333333) | ((crc << 2) & 0xcccccccc);
+ crc = ((crc >> 1) & 0x55555555) | ((crc << 1) & 0xaaaaaaaa);
+ /* swap bytes */
+ crc = (crc >> 16) | (crc << 16);
+ crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00);
+
+ return crc;
+}
+
+static int pm3393_set_rx_mode(struct cmac *cmac, struct t1_rx_mode *rm)
+{
+ int enabled = cmac->instance->enabled & MAC_DIRECTION_RX;
+ u32 rx_mode;
+
+ /* Disable MAC RX before reconfiguring it */
+ if (enabled)
+ pm3393_disable(cmac, MAC_DIRECTION_RX);
+
+ pmread(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_2, &rx_mode);
+ rx_mode &= ~(SUNI1x10GEXP_BITMSK_RXXG_PMODE |
+ SUNI1x10GEXP_BITMSK_RXXG_MHASH_EN);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_2,
+ (u16)rx_mode);
+
+ if (t1_rx_mode_promisc(rm)) {
+ /* Promiscuous mode. */
+ rx_mode |= SUNI1x10GEXP_BITMSK_RXXG_PMODE;
+ }
+ if (t1_rx_mode_allmulti(rm)) {
+ /* Accept all multicast. */
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_LOW, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDLOW, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDHIGH, 0xffff);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_HIGH, 0xffff);
+ rx_mode |= SUNI1x10GEXP_BITMSK_RXXG_MHASH_EN;
+ } else if (t1_rx_mode_mc_cnt(rm)) {
+ /* Accept one or more multicast(s). */
+ u8 *addr;
+ int bit;
+ u16 mc_filter[4] = { 0, };
+
+ while ((addr = t1_get_next_mcaddr(rm))) {
+ bit = (calc_crc(addr, ETH_ALEN) >> 23) & 0x3f; /* bit[23:28] */
+ mc_filter[bit >> 4] |= 1 << (bit & 0xf);
+ }
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_LOW, mc_filter[0]);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDLOW, mc_filter[1]);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDHIGH, mc_filter[2]);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_HIGH, mc_filter[3]);
+ rx_mode |= SUNI1x10GEXP_BITMSK_RXXG_MHASH_EN;
+ }
+
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_2, (u16)rx_mode);
+
+ if (enabled)
+ pm3393_enable(cmac, MAC_DIRECTION_RX);
+
+ return 0;
+}
+
+static int pm3393_get_speed_duplex_fc(struct cmac *cmac, int *speed,
+ int *duplex, int *fc)
+{
+ if (speed)
+ *speed = SPEED_10000;
+ if (duplex)
+ *duplex = DUPLEX_FULL;
+ if (fc)
+ *fc = cmac->instance->fc;
+ return 0;
+}
+
+static int pm3393_set_speed_duplex_fc(struct cmac *cmac, int speed, int duplex,
+ int fc)
+{
+ if (speed >= 0 && speed != SPEED_10000)
+ return -1;
+ if (duplex >= 0 && duplex != DUPLEX_FULL)
+ return -1;
+ if (fc & ~(PAUSE_TX | PAUSE_RX))
+ return -1;
+
+ if (fc != cmac->instance->fc) {
+ cmac->instance->fc = (u8) fc;
+ if (cmac->instance->enabled & MAC_DIRECTION_TX)
+ pm3393_enable(cmac, MAC_DIRECTION_TX);
+ }
+ return 0;
+}
+
+#define RMON_UPDATE(mac, name, stat_name) \
+ { \
+ t1_tpi_read((mac)->adapter, OFFSET(name), &val0); \
+ t1_tpi_read((mac)->adapter, OFFSET(((name)+1)), &val1); \
+ t1_tpi_read((mac)->adapter, OFFSET(((name)+2)), &val2); \
+ (mac)->stats.stat_name = ((u64)val0 & 0xffff) | \
+ (((u64)val1 & 0xffff) << 16) | \
+ (((u64)val2 & 0xff) << 32) | \
+ ((mac)->stats.stat_name & \
+ (~(u64)0 << 40)); \
+ if (ro & \
+ ((name - SUNI1x10GEXP_REG_MSTAT_COUNTER_0_LOW) >> 2)) \
+ (mac)->stats.stat_name += ((u64)1 << 40); \
+ }
+
+static const struct cmac_statistics *pm3393_update_statistics(struct cmac *mac,
+ int flag)
+{
+ u64 ro;
+ u32 val0, val1, val2, val3;
+
+ /* Snap the counters */
+ pmwrite(mac, SUNI1x10GEXP_REG_MSTAT_CONTROL,
+ SUNI1x10GEXP_BITMSK_MSTAT_SNAP);
+
+ /* Counter rollover, clear on read */
+ pmread(mac, SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_0, &val0);
+ pmread(mac, SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_1, &val1);
+ pmread(mac, SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_2, &val2);
+ pmread(mac, SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_3, &val3);
+ ro = ((u64)val0 & 0xffff) | (((u64)val1 & 0xffff) << 16) |
+ (((u64)val2 & 0xffff) << 32) | (((u64)val3 & 0xffff) << 48);
+
+ /* Rx stats */
+ RMON_UPDATE(mac, RxOctetsReceivedOK, RxOctetsOK);
+ RMON_UPDATE(mac, RxUnicastFramesReceivedOK, RxUnicastFramesOK);
+ RMON_UPDATE(mac, RxMulticastFramesReceivedOK, RxMulticastFramesOK);
+ RMON_UPDATE(mac, RxBroadcastFramesReceivedOK, RxBroadcastFramesOK);
+ RMON_UPDATE(mac, RxPAUSEMACCtrlFramesReceived, RxPauseFrames);
+ RMON_UPDATE(mac, RxFrameCheckSequenceErrors, RxFCSErrors);
+ RMON_UPDATE(mac, RxFramesLostDueToInternalMACErrors,
+ RxInternalMACRcvError);
+ RMON_UPDATE(mac, RxSymbolErrors, RxSymbolErrors);
+ RMON_UPDATE(mac, RxInRangeLengthErrors, RxInRangeLengthErrors);
+ RMON_UPDATE(mac, RxFramesTooLongErrors , RxFrameTooLongErrors);
+ RMON_UPDATE(mac, RxJabbers, RxJabberErrors);
+ RMON_UPDATE(mac, RxFragments, RxRuntErrors);
+ RMON_UPDATE(mac, RxUndersizedFrames, RxRuntErrors);
+
+ /* Tx stats */
+ RMON_UPDATE(mac, TxOctetsTransmittedOK, TxOctetsOK);
+ RMON_UPDATE(mac, TxFramesLostDueToInternalMACTransmissionError,
+ TxInternalMACXmitError);
+ RMON_UPDATE(mac, TxTransmitSystemError, TxFCSErrors);
+ RMON_UPDATE(mac, TxUnicastFramesTransmittedOK, TxUnicastFramesOK);
+ RMON_UPDATE(mac, TxMulticastFramesTransmittedOK, TxMulticastFramesOK);
+ RMON_UPDATE(mac, TxBroadcastFramesTransmittedOK, TxBroadcastFramesOK);
+ RMON_UPDATE(mac, TxPAUSEMACCtrlFramesTransmitted, TxPauseFrames);
+
+ return &mac->stats;
+}
+
+static int pm3393_macaddress_get(struct cmac *cmac, u8 mac_addr[6])
+{
+ memcpy(mac_addr, cmac->instance->mac_addr, 6);
+ return 0;
+}
+
+static int pm3393_macaddress_set(struct cmac *cmac, u8 ma[6])
+{
+ u32 val, lo, mid, hi, enabled = cmac->instance->enabled;
+
+ /*
+ * MAC addr: 00:07:43:00:13:09
+ *
+ * ma[5] = 0x09
+ * ma[4] = 0x13
+ * ma[3] = 0x00
+ * ma[2] = 0x43
+ * ma[1] = 0x07
+ * ma[0] = 0x00
+ *
+ * The PM3393 requires byte swapping and reverse order entry
+ * when programming MAC addresses:
+ *
+ * low_bits[15:0] = ma[1]:ma[0]
+ * mid_bits[31:16] = ma[3]:ma[2]
+ * high_bits[47:32] = ma[5]:ma[4]
+ */
+
+ /* Store local copy */
+ memcpy(cmac->instance->mac_addr, ma, 6);
+
+ lo = ((u32) ma[1] << 8) | (u32) ma[0];
+ mid = ((u32) ma[3] << 8) | (u32) ma[2];
+ hi = ((u32) ma[5] << 8) | (u32) ma[4];
+
+ /* Disable Rx/Tx MAC before configuring it. */
+ if (enabled)
+ pm3393_disable(cmac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
+
+ /* Set RXXG Station Address */
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_SA_15_0, lo);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_SA_31_16, mid);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_SA_47_32, hi);
+
+ /* Set TXXG Station Address */
+ pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_SA_15_0, lo);
+ pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_SA_31_16, mid);
+ pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_SA_47_32, hi);
+
+ /* Setup Exact Match Filter 1 with our MAC address
+ *
+ * Must disable exact match filter before configuring it.
+ */
+ pmread(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_0, &val);
+ val &= 0xff0f;
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_0, val);
+
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_LOW, lo);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_MID, mid);
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_HIGH, hi);
+
+ val |= 0x0090;
+ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_0, val);
+
+ if (enabled)
+ pm3393_enable(cmac, enabled);
+ return 0;
+}
+
+static void pm3393_destroy(struct cmac *cmac)
+{
+ kfree(cmac);
+}
+
+static struct cmac_ops pm3393_ops = {
+ .destroy = pm3393_destroy,
+ .reset = pm3393_reset,
+ .interrupt_enable = pm3393_interrupt_enable,
+ .interrupt_disable = pm3393_interrupt_disable,
+ .interrupt_clear = pm3393_interrupt_clear,
+ .interrupt_handler = pm3393_interrupt_handler,
+ .enable = pm3393_enable_port,
+ .disable = pm3393_disable,
+ .loopback_enable = pm3393_loopback_enable,
+ .loopback_disable = pm3393_loopback_disable,
+ .set_mtu = pm3393_set_mtu,
+ .set_rx_mode = pm3393_set_rx_mode,
+ .get_speed_duplex_fc = pm3393_get_speed_duplex_fc,
+ .set_speed_duplex_fc = pm3393_set_speed_duplex_fc,
+ .statistics_update = pm3393_update_statistics,
+ .macaddress_get = pm3393_macaddress_get,
+ .macaddress_set = pm3393_macaddress_set
+};
+
+static struct cmac *pm3393_mac_create(adapter_t *adapter, int index)
+{
+ struct cmac *cmac;
+
+ cmac = kmalloc(sizeof(*cmac) + sizeof(cmac_instance), GFP_KERNEL);
+ if (!cmac)
+ return NULL;
+ memset(cmac, 0, sizeof(*cmac));
+
+ cmac->ops = &pm3393_ops;
+ cmac->instance = (cmac_instance *) (cmac + 1);
+ cmac->adapter = adapter;
+ cmac->instance->fc = PAUSE_TX | PAUSE_RX;
+
+ t1_tpi_write(adapter, OFFSET(0x0001), 0x00008000);
+ t1_tpi_write(adapter, OFFSET(0x0001), 0x00000000);
+ t1_tpi_write(adapter, OFFSET(0x2308), 0x00009800);
+ t1_tpi_write(adapter, OFFSET(0x2305), 0x00001001); /* PL4IO Enable */
+ t1_tpi_write(adapter, OFFSET(0x2320), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x2321), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x2322), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x2323), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x2324), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x2325), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x2326), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x2327), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x2328), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x2329), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x232a), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x232b), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x232c), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x232d), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x232e), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x232f), 0x00008800);
+ t1_tpi_write(adapter, OFFSET(0x230d), 0x00009c00);
+ t1_tpi_write(adapter, OFFSET(0x2304), 0x00000202); /* PL4IO Calendar Repetitions */
+
+ t1_tpi_write(adapter, OFFSET(0x3200), 0x00008080); /* EFLX Enable */
+ t1_tpi_write(adapter, OFFSET(0x3210), 0x00000000); /* EFLX Channel Deprovision */
+ t1_tpi_write(adapter, OFFSET(0x3203), 0x00000000); /* EFLX Low Limit */
+ t1_tpi_write(adapter, OFFSET(0x3204), 0x00000040); /* EFLX High Limit */
+ t1_tpi_write(adapter, OFFSET(0x3205), 0x000002cc); /* EFLX Almost Full */
+ t1_tpi_write(adapter, OFFSET(0x3206), 0x00000199); /* EFLX Almost Empty */
+ t1_tpi_write(adapter, OFFSET(0x3207), 0x00000240); /* EFLX Cut Through Threshold */
+ t1_tpi_write(adapter, OFFSET(0x3202), 0x00000000); /* EFLX Indirect Register Update */
+ t1_tpi_write(adapter, OFFSET(0x3210), 0x00000001); /* EFLX Channel Provision */
+ t1_tpi_write(adapter, OFFSET(0x3208), 0x0000ffff); /* EFLX Undocumented */
+ t1_tpi_write(adapter, OFFSET(0x320a), 0x0000ffff); /* EFLX Undocumented */
+ t1_tpi_write(adapter, OFFSET(0x320c), 0x0000ffff); /* EFLX enable overflow interrupt The other bit are undocumented */
+ t1_tpi_write(adapter, OFFSET(0x320e), 0x0000ffff); /* EFLX Undocumented */
+
+ t1_tpi_write(adapter, OFFSET(0x2200), 0x0000c000); /* IFLX Configuration - enable */
+ t1_tpi_write(adapter, OFFSET(0x2201), 0x00000000); /* IFLX Channel Deprovision */
+ t1_tpi_write(adapter, OFFSET(0x220e), 0x00000000); /* IFLX Low Limit */
+ t1_tpi_write(adapter, OFFSET(0x220f), 0x00000100); /* IFLX High Limit */
+ t1_tpi_write(adapter, OFFSET(0x2210), 0x00000c00); /* IFLX Almost Full Limit */
+ t1_tpi_write(adapter, OFFSET(0x2211), 0x00000599); /* IFLX Almost Empty Limit */
+ t1_tpi_write(adapter, OFFSET(0x220d), 0x00000000); /* IFLX Indirect Register Update */
+ t1_tpi_write(adapter, OFFSET(0x2201), 0x00000001); /* IFLX Channel Provision */
+ t1_tpi_write(adapter, OFFSET(0x2203), 0x0000ffff); /* IFLX Undocumented */
+ t1_tpi_write(adapter, OFFSET(0x2205), 0x0000ffff); /* IFLX Undocumented */
+ t1_tpi_write(adapter, OFFSET(0x2209), 0x0000ffff); /* IFLX Enable overflow interrupt. The other bit are undocumented */
+
+ t1_tpi_write(adapter, OFFSET(0x2241), 0xfffffffe); /* PL4MOS Undocumented */
+ t1_tpi_write(adapter, OFFSET(0x2242), 0x0000ffff); /* PL4MOS Undocumented */
+ t1_tpi_write(adapter, OFFSET(0x2243), 0x00000008); /* PL4MOS Starving Burst Size */
+ t1_tpi_write(adapter, OFFSET(0x2244), 0x00000008); /* PL4MOS Hungry Burst Size */
+ t1_tpi_write(adapter, OFFSET(0x2245), 0x00000008); /* PL4MOS Transfer Size */
+ t1_tpi_write(adapter, OFFSET(0x2240), 0x00000005); /* PL4MOS Disable */
+
+ t1_tpi_write(adapter, OFFSET(0x2280), 0x00002103); /* PL4ODP Training Repeat and SOP rule */
+ t1_tpi_write(adapter, OFFSET(0x2284), 0x00000000); /* PL4ODP MAX_T setting */
+
+ t1_tpi_write(adapter, OFFSET(0x3280), 0x00000087); /* PL4IDU Enable data forward, port state machine. Set ALLOW_NON_ZERO_OLB */
+ t1_tpi_write(adapter, OFFSET(0x3282), 0x0000001f); /* PL4IDU Enable Dip4 check error interrupts */
+
+ t1_tpi_write(adapter, OFFSET(0x3040), 0x0c32); /* # TXXG Config */
+ /* For T1 use timer based Mac flow control. */
+ t1_tpi_write(adapter, OFFSET(0x304d), 0x8000);
+ t1_tpi_write(adapter, OFFSET(0x2040), 0x059c); /* # RXXG Config */
+ t1_tpi_write(adapter, OFFSET(0x2049), 0x0001); /* # RXXG Cut Through */
+ t1_tpi_write(adapter, OFFSET(0x2070), 0x0000); /* # Disable promiscuous mode */
+
+ /* Setup Exact Match Filter 0 to allow broadcast packets.
+ */
+ t1_tpi_write(adapter, OFFSET(0x206e), 0x0000); /* # Disable Match Enable bit */
+ t1_tpi_write(adapter, OFFSET(0x204a), 0xffff); /* # low addr */
+ t1_tpi_write(adapter, OFFSET(0x204b), 0xffff); /* # mid addr */
+ t1_tpi_write(adapter, OFFSET(0x204c), 0xffff); /* # high addr */
+ t1_tpi_write(adapter, OFFSET(0x206e), 0x0009); /* # Enable Match Enable bit */
+
+ t1_tpi_write(adapter, OFFSET(0x0003), 0x0000); /* # NO SOP/ PAD_EN setup */
+ t1_tpi_write(adapter, OFFSET(0x0100), 0x0ff0); /* # RXEQB disabled */
+ t1_tpi_write(adapter, OFFSET(0x0101), 0x0f0f); /* # No Preemphasis */
+
+ return cmac;
+}
+
+static int pm3393_mac_reset(adapter_t * adapter)
+{
+ u32 val;
+ u32 x;
+ u32 is_pl4_reset_finished;
+ u32 is_pl4_outof_lock;
+ u32 is_xaui_mabc_pll_locked;
+ u32 successful_reset;
+ int i;
+
+ /* The following steps are required to properly reset
+ * the PM3393. This information is provided in the
+ * PM3393 datasheet (Issue 2: November 2002)
+ * section 13.1 -- Device Reset.
+ *
+ * The PM3393 has three types of components that are
+ * individually reset:
+ *
+ * DRESETB - Digital circuitry
+ * PL4_ARESETB - PL4 analog circuitry
+ * XAUI_ARESETB - XAUI bus analog circuitry
+ *
+ * Steps to reset PM3393 using RSTB pin:
+ *
+ * 1. Assert RSTB pin low ( write 0 )
+ * 2. Wait at least 1ms to initiate a complete initialization of device.
+ * 3. Wait until all external clocks and REFSEL are stable.
+ * 4. Wait minimum of 1ms. (after external clocks and REFEL are stable)
+ * 5. De-assert RSTB ( write 1 )
+ * 6. Wait until internal timers to expires after ~14ms.
+ * - Allows analog clock synthesizer(PL4CSU) to stabilize to
+ * selected reference frequency before allowing the digital
+ * portion of the device to operate.
+ * 7. Wait at least 200us for XAUI interface to stabilize.
+ * 8. Verify the PM3393 came out of reset successfully.
+ * Set successful reset flag if everything worked else try again
+ * a few more times.
+ */
+
+ successful_reset = 0;
+ for (i = 0; i < 3 && !successful_reset; i++) {
+ /* 1 */
+ t1_tpi_read(adapter, A_ELMER0_GPO, &val);
+ val &= ~1;
+ t1_tpi_write(adapter, A_ELMER0_GPO, val);
+
+ /* 2 */
+ msleep(1);
+
+ /* 3 */
+ msleep(1);
+
+ /* 4 */
+ msleep(2 /*1 extra ms for safety */ );
+
+ /* 5 */
+ val |= 1;
+ t1_tpi_write(adapter, A_ELMER0_GPO, val);
+
+ /* 6 */
+ msleep(15 /*1 extra ms for safety */ );
+
+ /* 7 */
+ msleep(1);
+
+ /* 8 */
+
+ /* Has PL4 analog block come out of reset correctly? */
+ t1_tpi_read(adapter, OFFSET(SUNI1x10GEXP_REG_DEVICE_STATUS), &val);
+ is_pl4_reset_finished = (val & SUNI1x10GEXP_BITMSK_TOP_EXPIRED);
+
+ /* TBD XXX SUNI1x10GEXP_BITMSK_TOP_PL4_IS_DOOL gets locked later in the init sequence
+ * figure out why? */
+
+ /* Have all PL4 block clocks locked? */
+ x = (SUNI1x10GEXP_BITMSK_TOP_PL4_ID_DOOL
+ /*| SUNI1x10GEXP_BITMSK_TOP_PL4_IS_DOOL */ |
+ SUNI1x10GEXP_BITMSK_TOP_PL4_ID_ROOL |
+ SUNI1x10GEXP_BITMSK_TOP_PL4_IS_ROOL |
+ SUNI1x10GEXP_BITMSK_TOP_PL4_OUT_ROOL);
+ is_pl4_outof_lock = (val & x);
+
+ /* ??? If this fails, might be able to software reset the XAUI part
+ * and try to recover... thus saving us from doing another HW reset */
+ /* Has the XAUI MABC PLL circuitry stablized? */
+ is_xaui_mabc_pll_locked =
+ (val & SUNI1x10GEXP_BITMSK_TOP_SXRA_EXPIRED);
+
+ successful_reset = (is_pl4_reset_finished && !is_pl4_outof_lock
+ && is_xaui_mabc_pll_locked);
+ }
+ return successful_reset ? 0 : 1;
+}
+
+struct gmac t1_pm3393_ops = {
+ STATS_TICK_SECS,
+ pm3393_mac_create,
+ pm3393_mac_reset
+};
diff --git a/drivers/net/chelsio/regs.h b/drivers/net/chelsio/regs.h
new file mode 100644
index 000000000000..b90e11f40d1f
--- /dev/null
+++ b/drivers/net/chelsio/regs.h
@@ -0,0 +1,468 @@
+/*****************************************************************************
+ * *
+ * File: regs.h *
+ * $Revision: 1.8 $ *
+ * $Date: 2005/06/21 18:29:48 $ *
+ * Description: *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#ifndef _CXGB_REGS_H_
+#define _CXGB_REGS_H_
+
+/* SGE registers */
+#define A_SG_CONTROL 0x0
+
+#define S_CMDQ0_ENABLE 0
+#define V_CMDQ0_ENABLE(x) ((x) << S_CMDQ0_ENABLE)
+#define F_CMDQ0_ENABLE V_CMDQ0_ENABLE(1U)
+
+#define S_CMDQ1_ENABLE 1
+#define V_CMDQ1_ENABLE(x) ((x) << S_CMDQ1_ENABLE)
+#define F_CMDQ1_ENABLE V_CMDQ1_ENABLE(1U)
+
+#define S_FL0_ENABLE 2
+#define V_FL0_ENABLE(x) ((x) << S_FL0_ENABLE)
+#define F_FL0_ENABLE V_FL0_ENABLE(1U)
+
+#define S_FL1_ENABLE 3
+#define V_FL1_ENABLE(x) ((x) << S_FL1_ENABLE)
+#define F_FL1_ENABLE V_FL1_ENABLE(1U)
+
+#define S_CPL_ENABLE 4
+#define V_CPL_ENABLE(x) ((x) << S_CPL_ENABLE)
+#define F_CPL_ENABLE V_CPL_ENABLE(1U)
+
+#define S_RESPONSE_QUEUE_ENABLE 5
+#define V_RESPONSE_QUEUE_ENABLE(x) ((x) << S_RESPONSE_QUEUE_ENABLE)
+#define F_RESPONSE_QUEUE_ENABLE V_RESPONSE_QUEUE_ENABLE(1U)
+
+#define S_CMDQ_PRIORITY 6
+#define M_CMDQ_PRIORITY 0x3
+#define V_CMDQ_PRIORITY(x) ((x) << S_CMDQ_PRIORITY)
+#define G_CMDQ_PRIORITY(x) (((x) >> S_CMDQ_PRIORITY) & M_CMDQ_PRIORITY)
+
+#define S_DISABLE_CMDQ1_GTS 9
+#define V_DISABLE_CMDQ1_GTS(x) ((x) << S_DISABLE_CMDQ1_GTS)
+#define F_DISABLE_CMDQ1_GTS V_DISABLE_CMDQ1_GTS(1U)
+
+#define S_DISABLE_FL0_GTS 10
+#define V_DISABLE_FL0_GTS(x) ((x) << S_DISABLE_FL0_GTS)
+#define F_DISABLE_FL0_GTS V_DISABLE_FL0_GTS(1U)
+
+#define S_DISABLE_FL1_GTS 11
+#define V_DISABLE_FL1_GTS(x) ((x) << S_DISABLE_FL1_GTS)
+#define F_DISABLE_FL1_GTS V_DISABLE_FL1_GTS(1U)
+
+#define S_ENABLE_BIG_ENDIAN 12
+#define V_ENABLE_BIG_ENDIAN(x) ((x) << S_ENABLE_BIG_ENDIAN)
+#define F_ENABLE_BIG_ENDIAN V_ENABLE_BIG_ENDIAN(1U)
+
+#define S_ISCSI_COALESCE 14
+#define V_ISCSI_COALESCE(x) ((x) << S_ISCSI_COALESCE)
+#define F_ISCSI_COALESCE V_ISCSI_COALESCE(1U)
+
+#define S_RX_PKT_OFFSET 15
+#define V_RX_PKT_OFFSET(x) ((x) << S_RX_PKT_OFFSET)
+
+#define S_VLAN_XTRACT 18
+#define V_VLAN_XTRACT(x) ((x) << S_VLAN_XTRACT)
+#define F_VLAN_XTRACT V_VLAN_XTRACT(1U)
+
+#define A_SG_DOORBELL 0x4
+#define A_SG_CMD0BASELWR 0x8
+#define A_SG_CMD0BASEUPR 0xc
+#define A_SG_CMD1BASELWR 0x10
+#define A_SG_CMD1BASEUPR 0x14
+#define A_SG_FL0BASELWR 0x18
+#define A_SG_FL0BASEUPR 0x1c
+#define A_SG_FL1BASELWR 0x20
+#define A_SG_FL1BASEUPR 0x24
+#define A_SG_CMD0SIZE 0x28
+#define A_SG_FL0SIZE 0x2c
+#define A_SG_RSPSIZE 0x30
+#define A_SG_RSPBASELWR 0x34
+#define A_SG_RSPBASEUPR 0x38
+#define A_SG_FLTHRESHOLD 0x3c
+#define A_SG_RSPQUEUECREDIT 0x40
+#define A_SG_SLEEPING 0x48
+#define A_SG_INTRTIMER 0x4c
+#define A_SG_CMD1SIZE 0xb0
+#define A_SG_FL1SIZE 0xb4
+#define A_SG_INT_ENABLE 0xb8
+
+#define S_RESPQ_EXHAUSTED 0
+#define V_RESPQ_EXHAUSTED(x) ((x) << S_RESPQ_EXHAUSTED)
+#define F_RESPQ_EXHAUSTED V_RESPQ_EXHAUSTED(1U)
+
+#define S_RESPQ_OVERFLOW 1
+#define V_RESPQ_OVERFLOW(x) ((x) << S_RESPQ_OVERFLOW)
+#define F_RESPQ_OVERFLOW V_RESPQ_OVERFLOW(1U)
+
+#define S_FL_EXHAUSTED 2
+#define V_FL_EXHAUSTED(x) ((x) << S_FL_EXHAUSTED)
+#define F_FL_EXHAUSTED V_FL_EXHAUSTED(1U)
+
+#define S_PACKET_TOO_BIG 3
+#define V_PACKET_TOO_BIG(x) ((x) << S_PACKET_TOO_BIG)
+#define F_PACKET_TOO_BIG V_PACKET_TOO_BIG(1U)
+
+#define S_PACKET_MISMATCH 4
+#define V_PACKET_MISMATCH(x) ((x) << S_PACKET_MISMATCH)
+#define F_PACKET_MISMATCH V_PACKET_MISMATCH(1U)
+
+#define A_SG_INT_CAUSE 0xbc
+#define A_SG_RESPACCUTIMER 0xc0
+
+/* MC3 registers */
+
+#define S_READY 1
+#define V_READY(x) ((x) << S_READY)
+#define F_READY V_READY(1U)
+
+/* MC4 registers */
+
+#define A_MC4_CFG 0x180
+#define S_MC4_SLOW 25
+#define V_MC4_SLOW(x) ((x) << S_MC4_SLOW)
+#define F_MC4_SLOW V_MC4_SLOW(1U)
+
+/* TPI registers */
+
+#define A_TPI_ADDR 0x280
+#define A_TPI_WR_DATA 0x284
+#define A_TPI_RD_DATA 0x288
+#define A_TPI_CSR 0x28c
+
+#define S_TPIWR 0
+#define V_TPIWR(x) ((x) << S_TPIWR)
+#define F_TPIWR V_TPIWR(1U)
+
+#define S_TPIRDY 1
+#define V_TPIRDY(x) ((x) << S_TPIRDY)
+#define F_TPIRDY V_TPIRDY(1U)
+
+#define A_TPI_PAR 0x29c
+
+#define S_TPIPAR 0
+#define M_TPIPAR 0x7f
+#define V_TPIPAR(x) ((x) << S_TPIPAR)
+#define G_TPIPAR(x) (((x) >> S_TPIPAR) & M_TPIPAR)
+
+/* TP registers */
+
+#define A_TP_IN_CONFIG 0x300
+
+#define S_TP_IN_CSPI_CPL 3
+#define V_TP_IN_CSPI_CPL(x) ((x) << S_TP_IN_CSPI_CPL)
+#define F_TP_IN_CSPI_CPL V_TP_IN_CSPI_CPL(1U)
+
+#define S_TP_IN_CSPI_CHECK_IP_CSUM 5
+#define V_TP_IN_CSPI_CHECK_IP_CSUM(x) ((x) << S_TP_IN_CSPI_CHECK_IP_CSUM)
+#define F_TP_IN_CSPI_CHECK_IP_CSUM V_TP_IN_CSPI_CHECK_IP_CSUM(1U)
+
+#define S_TP_IN_CSPI_CHECK_TCP_CSUM 6
+#define V_TP_IN_CSPI_CHECK_TCP_CSUM(x) ((x) << S_TP_IN_CSPI_CHECK_TCP_CSUM)
+#define F_TP_IN_CSPI_CHECK_TCP_CSUM V_TP_IN_CSPI_CHECK_TCP_CSUM(1U)
+
+#define S_TP_IN_ESPI_ETHERNET 8
+#define V_TP_IN_ESPI_ETHERNET(x) ((x) << S_TP_IN_ESPI_ETHERNET)
+#define F_TP_IN_ESPI_ETHERNET V_TP_IN_ESPI_ETHERNET(1U)
+
+#define S_TP_IN_ESPI_CHECK_IP_CSUM 12
+#define V_TP_IN_ESPI_CHECK_IP_CSUM(x) ((x) << S_TP_IN_ESPI_CHECK_IP_CSUM)
+#define F_TP_IN_ESPI_CHECK_IP_CSUM V_TP_IN_ESPI_CHECK_IP_CSUM(1U)
+
+#define S_TP_IN_ESPI_CHECK_TCP_CSUM 13
+#define V_TP_IN_ESPI_CHECK_TCP_CSUM(x) ((x) << S_TP_IN_ESPI_CHECK_TCP_CSUM)
+#define F_TP_IN_ESPI_CHECK_TCP_CSUM V_TP_IN_ESPI_CHECK_TCP_CSUM(1U)
+
+#define S_OFFLOAD_DISABLE 14
+#define V_OFFLOAD_DISABLE(x) ((x) << S_OFFLOAD_DISABLE)
+#define F_OFFLOAD_DISABLE V_OFFLOAD_DISABLE(1U)
+
+#define A_TP_OUT_CONFIG 0x304
+
+#define S_TP_OUT_CSPI_CPL 2
+#define V_TP_OUT_CSPI_CPL(x) ((x) << S_TP_OUT_CSPI_CPL)
+#define F_TP_OUT_CSPI_CPL V_TP_OUT_CSPI_CPL(1U)
+
+#define S_TP_OUT_ESPI_ETHERNET 6
+#define V_TP_OUT_ESPI_ETHERNET(x) ((x) << S_TP_OUT_ESPI_ETHERNET)
+#define F_TP_OUT_ESPI_ETHERNET V_TP_OUT_ESPI_ETHERNET(1U)
+
+#define S_TP_OUT_ESPI_GENERATE_IP_CSUM 10
+#define V_TP_OUT_ESPI_GENERATE_IP_CSUM(x) ((x) << S_TP_OUT_ESPI_GENERATE_IP_CSUM)
+#define F_TP_OUT_ESPI_GENERATE_IP_CSUM V_TP_OUT_ESPI_GENERATE_IP_CSUM(1U)
+
+#define S_TP_OUT_ESPI_GENERATE_TCP_CSUM 11
+#define V_TP_OUT_ESPI_GENERATE_TCP_CSUM(x) ((x) << S_TP_OUT_ESPI_GENERATE_TCP_CSUM)
+#define F_TP_OUT_ESPI_GENERATE_TCP_CSUM V_TP_OUT_ESPI_GENERATE_TCP_CSUM(1U)
+
+#define A_TP_GLOBAL_CONFIG 0x308
+
+#define S_IP_TTL 0
+#define M_IP_TTL 0xff
+#define V_IP_TTL(x) ((x) << S_IP_TTL)
+
+#define S_TCP_CSUM 11
+#define V_TCP_CSUM(x) ((x) << S_TCP_CSUM)
+#define F_TCP_CSUM V_TCP_CSUM(1U)
+
+#define S_UDP_CSUM 12
+#define V_UDP_CSUM(x) ((x) << S_UDP_CSUM)
+#define F_UDP_CSUM V_UDP_CSUM(1U)
+
+#define S_IP_CSUM 13
+#define V_IP_CSUM(x) ((x) << S_IP_CSUM)
+#define F_IP_CSUM V_IP_CSUM(1U)
+
+#define S_PATH_MTU 15
+#define V_PATH_MTU(x) ((x) << S_PATH_MTU)
+#define F_PATH_MTU V_PATH_MTU(1U)
+
+#define S_5TUPLE_LOOKUP 17
+#define V_5TUPLE_LOOKUP(x) ((x) << S_5TUPLE_LOOKUP)
+
+#define S_SYN_COOKIE_PARAMETER 26
+#define V_SYN_COOKIE_PARAMETER(x) ((x) << S_SYN_COOKIE_PARAMETER)
+
+#define A_TP_PC_CONFIG 0x348
+#define S_DIS_TX_FILL_WIN_PUSH 12
+#define V_DIS_TX_FILL_WIN_PUSH(x) ((x) << S_DIS_TX_FILL_WIN_PUSH)
+#define F_DIS_TX_FILL_WIN_PUSH V_DIS_TX_FILL_WIN_PUSH(1U)
+
+#define S_TP_PC_REV 30
+#define M_TP_PC_REV 0x3
+#define G_TP_PC_REV(x) (((x) >> S_TP_PC_REV) & M_TP_PC_REV)
+#define A_TP_RESET 0x44c
+#define S_TP_RESET 0
+#define V_TP_RESET(x) ((x) << S_TP_RESET)
+#define F_TP_RESET V_TP_RESET(1U)
+
+#define A_TP_INT_ENABLE 0x470
+#define A_TP_INT_CAUSE 0x474
+#define A_TP_TX_DROP_CONFIG 0x4b8
+
+#define S_ENABLE_TX_DROP 31
+#define V_ENABLE_TX_DROP(x) ((x) << S_ENABLE_TX_DROP)
+#define F_ENABLE_TX_DROP V_ENABLE_TX_DROP(1U)
+
+#define S_ENABLE_TX_ERROR 30
+#define V_ENABLE_TX_ERROR(x) ((x) << S_ENABLE_TX_ERROR)
+#define F_ENABLE_TX_ERROR V_ENABLE_TX_ERROR(1U)
+
+#define S_DROP_TICKS_CNT 4
+#define V_DROP_TICKS_CNT(x) ((x) << S_DROP_TICKS_CNT)
+
+#define S_NUM_PKTS_DROPPED 0
+#define V_NUM_PKTS_DROPPED(x) ((x) << S_NUM_PKTS_DROPPED)
+
+/* CSPI registers */
+
+#define S_DIP4ERR 0
+#define V_DIP4ERR(x) ((x) << S_DIP4ERR)
+#define F_DIP4ERR V_DIP4ERR(1U)
+
+#define S_RXDROP 1
+#define V_RXDROP(x) ((x) << S_RXDROP)
+#define F_RXDROP V_RXDROP(1U)
+
+#define S_TXDROP 2
+#define V_TXDROP(x) ((x) << S_TXDROP)
+#define F_TXDROP V_TXDROP(1U)
+
+#define S_RXOVERFLOW 3
+#define V_RXOVERFLOW(x) ((x) << S_RXOVERFLOW)
+#define F_RXOVERFLOW V_RXOVERFLOW(1U)
+
+#define S_RAMPARITYERR 4
+#define V_RAMPARITYERR(x) ((x) << S_RAMPARITYERR)
+#define F_RAMPARITYERR V_RAMPARITYERR(1U)
+
+/* ESPI registers */
+
+#define A_ESPI_SCH_TOKEN0 0x880
+#define A_ESPI_SCH_TOKEN1 0x884
+#define A_ESPI_SCH_TOKEN2 0x888
+#define A_ESPI_SCH_TOKEN3 0x88c
+#define A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK 0x890
+#define A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK 0x894
+#define A_ESPI_CALENDAR_LENGTH 0x898
+#define A_PORT_CONFIG 0x89c
+
+#define S_RX_NPORTS 0
+#define V_RX_NPORTS(x) ((x) << S_RX_NPORTS)
+
+#define S_TX_NPORTS 8
+#define V_TX_NPORTS(x) ((x) << S_TX_NPORTS)
+
+#define A_ESPI_FIFO_STATUS_ENABLE 0x8a0
+
+#define S_RXSTATUSENABLE 0
+#define V_RXSTATUSENABLE(x) ((x) << S_RXSTATUSENABLE)
+#define F_RXSTATUSENABLE V_RXSTATUSENABLE(1U)
+
+#define S_INTEL1010MODE 4
+#define V_INTEL1010MODE(x) ((x) << S_INTEL1010MODE)
+#define F_INTEL1010MODE V_INTEL1010MODE(1U)
+
+#define A_ESPI_MAXBURST1_MAXBURST2 0x8a8
+#define A_ESPI_TRAIN 0x8ac
+#define A_ESPI_INTR_STATUS 0x8c8
+
+#define S_DIP2PARITYERR 5
+#define V_DIP2PARITYERR(x) ((x) << S_DIP2PARITYERR)
+#define F_DIP2PARITYERR V_DIP2PARITYERR(1U)
+
+#define A_ESPI_INTR_ENABLE 0x8cc
+#define A_RX_DROP_THRESHOLD 0x8d0
+#define A_ESPI_RX_RESET 0x8ec
+#define A_ESPI_MISC_CONTROL 0x8f0
+
+#define S_OUT_OF_SYNC_COUNT 0
+#define V_OUT_OF_SYNC_COUNT(x) ((x) << S_OUT_OF_SYNC_COUNT)
+
+#define S_DIP2_PARITY_ERR_THRES 5
+#define V_DIP2_PARITY_ERR_THRES(x) ((x) << S_DIP2_PARITY_ERR_THRES)
+
+#define S_DIP4_THRES 9
+#define V_DIP4_THRES(x) ((x) << S_DIP4_THRES)
+
+#define S_MONITORED_PORT_NUM 25
+#define V_MONITORED_PORT_NUM(x) ((x) << S_MONITORED_PORT_NUM)
+
+#define S_MONITORED_DIRECTION 27
+#define V_MONITORED_DIRECTION(x) ((x) << S_MONITORED_DIRECTION)
+#define F_MONITORED_DIRECTION V_MONITORED_DIRECTION(1U)
+
+#define S_MONITORED_INTERFACE 28
+#define V_MONITORED_INTERFACE(x) ((x) << S_MONITORED_INTERFACE)
+#define F_MONITORED_INTERFACE V_MONITORED_INTERFACE(1U)
+
+#define A_ESPI_DIP2_ERR_COUNT 0x8f4
+#define A_ESPI_CMD_ADDR 0x8f8
+
+#define S_WRITE_DATA 0
+#define V_WRITE_DATA(x) ((x) << S_WRITE_DATA)
+
+#define S_REGISTER_OFFSET 8
+#define V_REGISTER_OFFSET(x) ((x) << S_REGISTER_OFFSET)
+
+#define S_CHANNEL_ADDR 12
+#define V_CHANNEL_ADDR(x) ((x) << S_CHANNEL_ADDR)
+
+#define S_MODULE_ADDR 16
+#define V_MODULE_ADDR(x) ((x) << S_MODULE_ADDR)
+
+#define S_BUNDLE_ADDR 20
+#define V_BUNDLE_ADDR(x) ((x) << S_BUNDLE_ADDR)
+
+#define S_SPI4_COMMAND 24
+#define V_SPI4_COMMAND(x) ((x) << S_SPI4_COMMAND)
+
+#define A_ESPI_GOSTAT 0x8fc
+#define S_ESPI_CMD_BUSY 8
+#define V_ESPI_CMD_BUSY(x) ((x) << S_ESPI_CMD_BUSY)
+#define F_ESPI_CMD_BUSY V_ESPI_CMD_BUSY(1U)
+
+/* PL registers */
+
+#define A_PL_ENABLE 0xa00
+
+#define S_PL_INTR_SGE_ERR 0
+#define V_PL_INTR_SGE_ERR(x) ((x) << S_PL_INTR_SGE_ERR)
+#define F_PL_INTR_SGE_ERR V_PL_INTR_SGE_ERR(1U)
+
+#define S_PL_INTR_SGE_DATA 1
+#define V_PL_INTR_SGE_DATA(x) ((x) << S_PL_INTR_SGE_DATA)
+#define F_PL_INTR_SGE_DATA V_PL_INTR_SGE_DATA(1U)
+
+#define S_PL_INTR_TP 6
+#define V_PL_INTR_TP(x) ((x) << S_PL_INTR_TP)
+#define F_PL_INTR_TP V_PL_INTR_TP(1U)
+
+#define S_PL_INTR_ESPI 8
+#define V_PL_INTR_ESPI(x) ((x) << S_PL_INTR_ESPI)
+#define F_PL_INTR_ESPI V_PL_INTR_ESPI(1U)
+
+#define S_PL_INTR_PCIX 10
+#define V_PL_INTR_PCIX(x) ((x) << S_PL_INTR_PCIX)
+#define F_PL_INTR_PCIX V_PL_INTR_PCIX(1U)
+
+#define S_PL_INTR_EXT 11
+#define V_PL_INTR_EXT(x) ((x) << S_PL_INTR_EXT)
+#define F_PL_INTR_EXT V_PL_INTR_EXT(1U)
+
+#define A_PL_CAUSE 0xa04
+
+/* MC5 registers */
+
+#define A_MC5_CONFIG 0xc04
+
+#define S_TCAM_RESET 1
+#define V_TCAM_RESET(x) ((x) << S_TCAM_RESET)
+#define F_TCAM_RESET V_TCAM_RESET(1U)
+
+#define S_M_BUS_ENABLE 5
+#define V_M_BUS_ENABLE(x) ((x) << S_M_BUS_ENABLE)
+#define F_M_BUS_ENABLE V_M_BUS_ENABLE(1U)
+
+/* PCICFG registers */
+
+#define A_PCICFG_PM_CSR 0x44
+#define A_PCICFG_VPD_ADDR 0x4a
+
+#define S_VPD_OP_FLAG 15
+#define V_VPD_OP_FLAG(x) ((x) << S_VPD_OP_FLAG)
+#define F_VPD_OP_FLAG V_VPD_OP_FLAG(1U)
+
+#define A_PCICFG_VPD_DATA 0x4c
+
+#define A_PCICFG_INTR_ENABLE 0xf4
+#define A_PCICFG_INTR_CAUSE 0xf8
+
+#define A_PCICFG_MODE 0xfc
+
+#define S_PCI_MODE_64BIT 0
+#define V_PCI_MODE_64BIT(x) ((x) << S_PCI_MODE_64BIT)
+#define F_PCI_MODE_64BIT V_PCI_MODE_64BIT(1U)
+
+#define S_PCI_MODE_PCIX 5
+#define V_PCI_MODE_PCIX(x) ((x) << S_PCI_MODE_PCIX)
+#define F_PCI_MODE_PCIX V_PCI_MODE_PCIX(1U)
+
+#define S_PCI_MODE_CLK 6
+#define M_PCI_MODE_CLK 0x3
+#define G_PCI_MODE_CLK(x) (((x) >> S_PCI_MODE_CLK) & M_PCI_MODE_CLK)
+
+#endif /* _CXGB_REGS_H_ */
diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c
new file mode 100644
index 000000000000..53b41d99b00b
--- /dev/null
+++ b/drivers/net/chelsio/sge.c
@@ -0,0 +1,1684 @@
+/*****************************************************************************
+ * *
+ * File: sge.c *
+ * $Revision: 1.26 $ *
+ * $Date: 2005/06/21 18:29:48 $ *
+ * Description: *
+ * DMA engine. *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#include "common.h"
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/if_arp.h>
+
+#include "cpl5_cmd.h"
+#include "sge.h"
+#include "regs.h"
+#include "espi.h"
+
+
+#ifdef NETIF_F_TSO
+#include <linux/tcp.h>
+#endif
+
+#define SGE_CMDQ_N 2
+#define SGE_FREELQ_N 2
+#define SGE_CMDQ0_E_N 1024
+#define SGE_CMDQ1_E_N 128
+#define SGE_FREEL_SIZE 4096
+#define SGE_JUMBO_FREEL_SIZE 512
+#define SGE_FREEL_REFILL_THRESH 16
+#define SGE_RESPQ_E_N 1024
+#define SGE_INTRTIMER_NRES 1000
+#define SGE_RX_COPY_THRES 256
+#define SGE_RX_SM_BUF_SIZE 1536
+
+# define SGE_RX_DROP_THRES 2
+
+#define SGE_RESPQ_REPLENISH_THRES (SGE_RESPQ_E_N / 4)
+
+/*
+ * Period of the TX buffer reclaim timer. This timer does not need to run
+ * frequently as TX buffers are usually reclaimed by new TX packets.
+ */
+#define TX_RECLAIM_PERIOD (HZ / 4)
+
+#ifndef NET_IP_ALIGN
+# define NET_IP_ALIGN 2
+#endif
+
+#define M_CMD_LEN 0x7fffffff
+#define V_CMD_LEN(v) (v)
+#define G_CMD_LEN(v) ((v) & M_CMD_LEN)
+#define V_CMD_GEN1(v) ((v) << 31)
+#define V_CMD_GEN2(v) (v)
+#define F_CMD_DATAVALID (1 << 1)
+#define F_CMD_SOP (1 << 2)
+#define V_CMD_EOP(v) ((v) << 3)
+
+/*
+ * Command queue, receive buffer list, and response queue descriptors.
+ */
+#if defined(__BIG_ENDIAN_BITFIELD)
+struct cmdQ_e {
+ u32 addr_lo;
+ u32 len_gen;
+ u32 flags;
+ u32 addr_hi;
+};
+
+struct freelQ_e {
+ u32 addr_lo;
+ u32 len_gen;
+ u32 gen2;
+ u32 addr_hi;
+};
+
+struct respQ_e {
+ u32 Qsleeping : 4;
+ u32 Cmdq1CreditReturn : 5;
+ u32 Cmdq1DmaComplete : 5;
+ u32 Cmdq0CreditReturn : 5;
+ u32 Cmdq0DmaComplete : 5;
+ u32 FreelistQid : 2;
+ u32 CreditValid : 1;
+ u32 DataValid : 1;
+ u32 Offload : 1;
+ u32 Eop : 1;
+ u32 Sop : 1;
+ u32 GenerationBit : 1;
+ u32 BufferLength;
+};
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+struct cmdQ_e {
+ u32 len_gen;
+ u32 addr_lo;
+ u32 addr_hi;
+ u32 flags;
+};
+
+struct freelQ_e {
+ u32 len_gen;
+ u32 addr_lo;
+ u32 addr_hi;
+ u32 gen2;
+};
+
+struct respQ_e {
+ u32 BufferLength;
+ u32 GenerationBit : 1;
+ u32 Sop : 1;
+ u32 Eop : 1;
+ u32 Offload : 1;
+ u32 DataValid : 1;
+ u32 CreditValid : 1;
+ u32 FreelistQid : 2;
+ u32 Cmdq0DmaComplete : 5;
+ u32 Cmdq0CreditReturn : 5;
+ u32 Cmdq1DmaComplete : 5;
+ u32 Cmdq1CreditReturn : 5;
+ u32 Qsleeping : 4;
+} ;
+#endif
+
+/*
+ * SW Context Command and Freelist Queue Descriptors
+ */
+struct cmdQ_ce {
+ struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(dma_addr);
+ DECLARE_PCI_UNMAP_LEN(dma_len);
+};
+
+struct freelQ_ce {
+ struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(dma_addr);
+ DECLARE_PCI_UNMAP_LEN(dma_len);
+};
+
+/*
+ * SW command, freelist and response rings
+ */
+struct cmdQ {
+ unsigned long status; /* HW DMA fetch status */
+ unsigned int in_use; /* # of in-use command descriptors */
+ unsigned int size; /* # of descriptors */
+ unsigned int processed; /* total # of descs HW has processed */
+ unsigned int cleaned; /* total # of descs SW has reclaimed */
+ unsigned int stop_thres; /* SW TX queue suspend threshold */
+ u16 pidx; /* producer index (SW) */
+ u16 cidx; /* consumer index (HW) */
+ u8 genbit; /* current generation (=valid) bit */
+ u8 sop; /* is next entry start of packet? */
+ struct cmdQ_e *entries; /* HW command descriptor Q */
+ struct cmdQ_ce *centries; /* SW command context descriptor Q */
+ spinlock_t lock; /* Lock to protect cmdQ enqueuing */
+ dma_addr_t dma_addr; /* DMA addr HW command descriptor Q */
+};
+
+struct freelQ {
+ unsigned int credits; /* # of available RX buffers */
+ unsigned int size; /* free list capacity */
+ u16 pidx; /* producer index (SW) */
+ u16 cidx; /* consumer index (HW) */
+ u16 rx_buffer_size; /* Buffer size on this free list */
+ u16 dma_offset; /* DMA offset to align IP headers */
+ u16 recycleq_idx; /* skb recycle q to use */
+ u8 genbit; /* current generation (=valid) bit */
+ struct freelQ_e *entries; /* HW freelist descriptor Q */
+ struct freelQ_ce *centries; /* SW freelist context descriptor Q */
+ dma_addr_t dma_addr; /* DMA addr HW freelist descriptor Q */
+};
+
+struct respQ {
+ unsigned int credits; /* credits to be returned to SGE */
+ unsigned int size; /* # of response Q descriptors */
+ u16 cidx; /* consumer index (SW) */
+ u8 genbit; /* current generation(=valid) bit */
+ struct respQ_e *entries; /* HW response descriptor Q */
+ dma_addr_t dma_addr; /* DMA addr HW response descriptor Q */
+};
+
+/* Bit flags for cmdQ.status */
+enum {
+ CMDQ_STAT_RUNNING = 1, /* fetch engine is running */
+ CMDQ_STAT_LAST_PKT_DB = 2 /* last packet rung the doorbell */
+};
+
+/*
+ * Main SGE data structure
+ *
+ * Interrupts are handled by a single CPU and it is likely that on a MP system
+ * the application is migrated to another CPU. In that scenario, we try to
+ * seperate the RX(in irq context) and TX state in order to decrease memory
+ * contention.
+ */
+struct sge {
+ struct adapter *adapter; /* adapter backpointer */
+ struct net_device *netdev; /* netdevice backpointer */
+ struct freelQ freelQ[SGE_FREELQ_N]; /* buffer free lists */
+ struct respQ respQ; /* response Q */
+ unsigned long stopped_tx_queues; /* bitmap of suspended Tx queues */
+ unsigned int rx_pkt_pad; /* RX padding for L2 packets */
+ unsigned int jumbo_fl; /* jumbo freelist Q index */
+ unsigned int intrtimer_nres; /* no-resource interrupt timer */
+ unsigned int fixed_intrtimer;/* non-adaptive interrupt timer */
+ struct timer_list tx_reclaim_timer; /* reclaims TX buffers */
+ struct timer_list espibug_timer;
+ unsigned int espibug_timeout;
+ struct sk_buff *espibug_skb;
+ u32 sge_control; /* shadow value of sge control reg */
+ struct sge_intr_counts stats;
+ struct sge_port_stats port_stats[MAX_NPORTS];
+ struct cmdQ cmdQ[SGE_CMDQ_N] ____cacheline_aligned_in_smp;
+};
+
+/*
+ * PIO to indicate that memory mapped Q contains valid descriptor(s).
+ */
+static inline void doorbell_pio(struct adapter *adapter, u32 val)
+{
+ wmb();
+ writel(val, adapter->regs + A_SG_DOORBELL);
+}
+
+/*
+ * Frees all RX buffers on the freelist Q. The caller must make sure that
+ * the SGE is turned off before calling this function.
+ */
+static void free_freelQ_buffers(struct pci_dev *pdev, struct freelQ *q)
+{
+ unsigned int cidx = q->cidx;
+
+ while (q->credits--) {
+ struct freelQ_ce *ce = &q->centries[cidx];
+
+ pci_unmap_single(pdev, pci_unmap_addr(ce, dma_addr),
+ pci_unmap_len(ce, dma_len),
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(ce->skb);
+ ce->skb = NULL;
+ if (++cidx == q->size)
+ cidx = 0;
+ }
+}
+
+/*
+ * Free RX free list and response queue resources.
+ */
+static void free_rx_resources(struct sge *sge)
+{
+ struct pci_dev *pdev = sge->adapter->pdev;
+ unsigned int size, i;
+
+ if (sge->respQ.entries) {
+ size = sizeof(struct respQ_e) * sge->respQ.size;
+ pci_free_consistent(pdev, size, sge->respQ.entries,
+ sge->respQ.dma_addr);
+ }
+
+ for (i = 0; i < SGE_FREELQ_N; i++) {
+ struct freelQ *q = &sge->freelQ[i];
+
+ if (q->centries) {
+ free_freelQ_buffers(pdev, q);
+ kfree(q->centries);
+ }
+ if (q->entries) {
+ size = sizeof(struct freelQ_e) * q->size;
+ pci_free_consistent(pdev, size, q->entries,
+ q->dma_addr);
+ }
+ }
+}
+
+/*
+ * Allocates basic RX resources, consisting of memory mapped freelist Qs and a
+ * response queue.
+ */
+static int alloc_rx_resources(struct sge *sge, struct sge_params *p)
+{
+ struct pci_dev *pdev = sge->adapter->pdev;
+ unsigned int size, i;
+
+ for (i = 0; i < SGE_FREELQ_N; i++) {
+ struct freelQ *q = &sge->freelQ[i];
+
+ q->genbit = 1;
+ q->size = p->freelQ_size[i];
+ q->dma_offset = sge->rx_pkt_pad ? 0 : NET_IP_ALIGN;
+ size = sizeof(struct freelQ_e) * q->size;
+ q->entries = (struct freelQ_e *)
+ pci_alloc_consistent(pdev, size, &q->dma_addr);
+ if (!q->entries)
+ goto err_no_mem;
+ memset(q->entries, 0, size);
+ size = sizeof(struct freelQ_ce) * q->size;
+ q->centries = kmalloc(size, GFP_KERNEL);
+ if (!q->centries)
+ goto err_no_mem;
+ memset(q->centries, 0, size);
+ }
+
+ /*
+ * Calculate the buffer sizes for the two free lists. FL0 accommodates
+ * regular sized Ethernet frames, FL1 is sized not to exceed 16K,
+ * including all the sk_buff overhead.
+ *
+ * Note: For T2 FL0 and FL1 are reversed.
+ */
+ sge->freelQ[!sge->jumbo_fl].rx_buffer_size = SGE_RX_SM_BUF_SIZE +
+ sizeof(struct cpl_rx_data) +
+ sge->freelQ[!sge->jumbo_fl].dma_offset;
+ sge->freelQ[sge->jumbo_fl].rx_buffer_size = (16 * 1024) -
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+ /*
+ * Setup which skb recycle Q should be used when recycling buffers from
+ * each free list.
+ */
+ sge->freelQ[!sge->jumbo_fl].recycleq_idx = 0;
+ sge->freelQ[sge->jumbo_fl].recycleq_idx = 1;
+
+ sge->respQ.genbit = 1;
+ sge->respQ.size = SGE_RESPQ_E_N;
+ sge->respQ.credits = 0;
+ size = sizeof(struct respQ_e) * sge->respQ.size;
+ sge->respQ.entries = (struct respQ_e *)
+ pci_alloc_consistent(pdev, size, &sge->respQ.dma_addr);
+ if (!sge->respQ.entries)
+ goto err_no_mem;
+ memset(sge->respQ.entries, 0, size);
+ return 0;
+
+err_no_mem:
+ free_rx_resources(sge);
+ return -ENOMEM;
+}
+
+/*
+ * Reclaims n TX descriptors and frees the buffers associated with them.
+ */
+static void free_cmdQ_buffers(struct sge *sge, struct cmdQ *q, unsigned int n)
+{
+ struct cmdQ_ce *ce;
+ struct pci_dev *pdev = sge->adapter->pdev;
+ unsigned int cidx = q->cidx;
+
+ q->in_use -= n;
+ ce = &q->centries[cidx];
+ while (n--) {
+ if (q->sop)
+ pci_unmap_single(pdev, pci_unmap_addr(ce, dma_addr),
+ pci_unmap_len(ce, dma_len),
+ PCI_DMA_TODEVICE);
+ else
+ pci_unmap_page(pdev, pci_unmap_addr(ce, dma_addr),
+ pci_unmap_len(ce, dma_len),
+ PCI_DMA_TODEVICE);
+ q->sop = 0;
+ if (ce->skb) {
+ dev_kfree_skb(ce->skb);
+ q->sop = 1;
+ }
+ ce++;
+ if (++cidx == q->size) {
+ cidx = 0;
+ ce = q->centries;
+ }
+ }
+ q->cidx = cidx;
+}
+
+/*
+ * Free TX resources.
+ *
+ * Assumes that SGE is stopped and all interrupts are disabled.
+ */
+static void free_tx_resources(struct sge *sge)
+{
+ struct pci_dev *pdev = sge->adapter->pdev;
+ unsigned int size, i;
+
+ for (i = 0; i < SGE_CMDQ_N; i++) {
+ struct cmdQ *q = &sge->cmdQ[i];
+
+ if (q->centries) {
+ if (q->in_use)
+ free_cmdQ_buffers(sge, q, q->in_use);
+ kfree(q->centries);
+ }
+ if (q->entries) {
+ size = sizeof(struct cmdQ_e) * q->size;
+ pci_free_consistent(pdev, size, q->entries,
+ q->dma_addr);
+ }
+ }
+}
+
+/*
+ * Allocates basic TX resources, consisting of memory mapped command Qs.
+ */
+static int alloc_tx_resources(struct sge *sge, struct sge_params *p)
+{
+ struct pci_dev *pdev = sge->adapter->pdev;
+ unsigned int size, i;
+
+ for (i = 0; i < SGE_CMDQ_N; i++) {
+ struct cmdQ *q = &sge->cmdQ[i];
+
+ q->genbit = 1;
+ q->sop = 1;
+ q->size = p->cmdQ_size[i];
+ q->in_use = 0;
+ q->status = 0;
+ q->processed = q->cleaned = 0;
+ q->stop_thres = 0;
+ spin_lock_init(&q->lock);
+ size = sizeof(struct cmdQ_e) * q->size;
+ q->entries = (struct cmdQ_e *)
+ pci_alloc_consistent(pdev, size, &q->dma_addr);
+ if (!q->entries)
+ goto err_no_mem;
+ memset(q->entries, 0, size);
+ size = sizeof(struct cmdQ_ce) * q->size;
+ q->centries = kmalloc(size, GFP_KERNEL);
+ if (!q->centries)
+ goto err_no_mem;
+ memset(q->centries, 0, size);
+ }
+
+ /*
+ * CommandQ 0 handles Ethernet and TOE packets, while queue 1 is TOE
+ * only. For queue 0 set the stop threshold so we can handle one more
+ * packet from each port, plus reserve an additional 24 entries for
+ * Ethernet packets only. Queue 1 never suspends nor do we reserve
+ * space for Ethernet packets.
+ */
+ sge->cmdQ[0].stop_thres = sge->adapter->params.nports *
+ (MAX_SKB_FRAGS + 1);
+ return 0;
+
+err_no_mem:
+ free_tx_resources(sge);
+ return -ENOMEM;
+}
+
+static inline void setup_ring_params(struct adapter *adapter, u64 addr,
+ u32 size, int base_reg_lo,
+ int base_reg_hi, int size_reg)
+{
+ writel((u32)addr, adapter->regs + base_reg_lo);
+ writel(addr >> 32, adapter->regs + base_reg_hi);
+ writel(size, adapter->regs + size_reg);
+}
+
+/*
+ * Enable/disable VLAN acceleration.
+ */
+void t1_set_vlan_accel(struct adapter *adapter, int on_off)
+{
+ struct sge *sge = adapter->sge;
+
+ sge->sge_control &= ~F_VLAN_XTRACT;
+ if (on_off)
+ sge->sge_control |= F_VLAN_XTRACT;
+ if (adapter->open_device_map) {
+ writel(sge->sge_control, adapter->regs + A_SG_CONTROL);
+ readl(adapter->regs + A_SG_CONTROL); /* flush */
+ }
+}
+
+/*
+ * Programs the various SGE registers. However, the engine is not yet enabled,
+ * but sge->sge_control is setup and ready to go.
+ */
+static void configure_sge(struct sge *sge, struct sge_params *p)
+{
+ struct adapter *ap = sge->adapter;
+
+ writel(0, ap->regs + A_SG_CONTROL);
+ setup_ring_params(ap, sge->cmdQ[0].dma_addr, sge->cmdQ[0].size,
+ A_SG_CMD0BASELWR, A_SG_CMD0BASEUPR, A_SG_CMD0SIZE);
+ setup_ring_params(ap, sge->cmdQ[1].dma_addr, sge->cmdQ[1].size,
+ A_SG_CMD1BASELWR, A_SG_CMD1BASEUPR, A_SG_CMD1SIZE);
+ setup_ring_params(ap, sge->freelQ[0].dma_addr,
+ sge->freelQ[0].size, A_SG_FL0BASELWR,
+ A_SG_FL0BASEUPR, A_SG_FL0SIZE);
+ setup_ring_params(ap, sge->freelQ[1].dma_addr,
+ sge->freelQ[1].size, A_SG_FL1BASELWR,
+ A_SG_FL1BASEUPR, A_SG_FL1SIZE);
+
+ /* The threshold comparison uses <. */
+ writel(SGE_RX_SM_BUF_SIZE + 1, ap->regs + A_SG_FLTHRESHOLD);
+
+ setup_ring_params(ap, sge->respQ.dma_addr, sge->respQ.size,
+ A_SG_RSPBASELWR, A_SG_RSPBASEUPR, A_SG_RSPSIZE);
+ writel((u32)sge->respQ.size - 1, ap->regs + A_SG_RSPQUEUECREDIT);
+
+ sge->sge_control = F_CMDQ0_ENABLE | F_CMDQ1_ENABLE | F_FL0_ENABLE |
+ F_FL1_ENABLE | F_CPL_ENABLE | F_RESPONSE_QUEUE_ENABLE |
+ V_CMDQ_PRIORITY(2) | F_DISABLE_CMDQ1_GTS | F_ISCSI_COALESCE |
+ F_DISABLE_FL0_GTS | F_DISABLE_FL1_GTS |
+ V_RX_PKT_OFFSET(sge->rx_pkt_pad);
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ sge->sge_control |= F_ENABLE_BIG_ENDIAN;
+#endif
+
+ /* Initialize no-resource timer */
+ sge->intrtimer_nres = SGE_INTRTIMER_NRES * core_ticks_per_usec(ap);
+
+ t1_sge_set_coalesce_params(sge, p);
+}
+
+/*
+ * Return the payload capacity of the jumbo free-list buffers.
+ */
+static inline unsigned int jumbo_payload_capacity(const struct sge *sge)
+{
+ return sge->freelQ[sge->jumbo_fl].rx_buffer_size -
+ sge->freelQ[sge->jumbo_fl].dma_offset -
+ sizeof(struct cpl_rx_data);
+}
+
+/*
+ * Frees all SGE related resources and the sge structure itself
+ */
+void t1_sge_destroy(struct sge *sge)
+{
+ if (sge->espibug_skb)
+ kfree_skb(sge->espibug_skb);
+
+ free_tx_resources(sge);
+ free_rx_resources(sge);
+ kfree(sge);
+}
+
+/*
+ * Allocates new RX buffers on the freelist Q (and tracks them on the freelist
+ * context Q) until the Q is full or alloc_skb fails.
+ *
+ * It is possible that the generation bits already match, indicating that the
+ * buffer is already valid and nothing needs to be done. This happens when we
+ * copied a received buffer into a new sk_buff during the interrupt processing.
+ *
+ * If the SGE doesn't automatically align packets properly (!sge->rx_pkt_pad),
+ * we specify a RX_OFFSET in order to make sure that the IP header is 4B
+ * aligned.
+ */
+static void refill_free_list(struct sge *sge, struct freelQ *q)
+{
+ struct pci_dev *pdev = sge->adapter->pdev;
+ struct freelQ_ce *ce = &q->centries[q->pidx];
+ struct freelQ_e *e = &q->entries[q->pidx];
+ unsigned int dma_len = q->rx_buffer_size - q->dma_offset;
+
+
+ while (q->credits < q->size) {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+
+ skb = alloc_skb(q->rx_buffer_size, GFP_ATOMIC);
+ if (!skb)
+ break;
+
+ skb_reserve(skb, q->dma_offset);
+ mapping = pci_map_single(pdev, skb->data, dma_len,
+ PCI_DMA_FROMDEVICE);
+ ce->skb = skb;
+ pci_unmap_addr_set(ce, dma_addr, mapping);
+ pci_unmap_len_set(ce, dma_len, dma_len);
+ e->addr_lo = (u32)mapping;
+ e->addr_hi = (u64)mapping >> 32;
+ e->len_gen = V_CMD_LEN(dma_len) | V_CMD_GEN1(q->genbit);
+ wmb();
+ e->gen2 = V_CMD_GEN2(q->genbit);
+
+ e++;
+ ce++;
+ if (++q->pidx == q->size) {
+ q->pidx = 0;
+ q->genbit ^= 1;
+ ce = q->centries;
+ e = q->entries;
+ }
+ q->credits++;
+ }
+
+}
+
+/*
+ * Calls refill_free_list for both free lists. If we cannot fill at least 1/4
+ * of both rings, we go into 'few interrupt mode' in order to give the system
+ * time to free up resources.
+ */
+static void freelQs_empty(struct sge *sge)
+{
+ struct adapter *adapter = sge->adapter;
+ u32 irq_reg = readl(adapter->regs + A_SG_INT_ENABLE);
+ u32 irqholdoff_reg;
+
+ refill_free_list(sge, &sge->freelQ[0]);
+ refill_free_list(sge, &sge->freelQ[1]);
+
+ if (sge->freelQ[0].credits > (sge->freelQ[0].size >> 2) &&
+ sge->freelQ[1].credits > (sge->freelQ[1].size >> 2)) {
+ irq_reg |= F_FL_EXHAUSTED;
+ irqholdoff_reg = sge->fixed_intrtimer;
+ } else {
+ /* Clear the F_FL_EXHAUSTED interrupts for now */
+ irq_reg &= ~F_FL_EXHAUSTED;
+ irqholdoff_reg = sge->intrtimer_nres;
+ }
+ writel(irqholdoff_reg, adapter->regs + A_SG_INTRTIMER);
+ writel(irq_reg, adapter->regs + A_SG_INT_ENABLE);
+
+ /* We reenable the Qs to force a freelist GTS interrupt later */
+ doorbell_pio(adapter, F_FL0_ENABLE | F_FL1_ENABLE);
+}
+
+#define SGE_PL_INTR_MASK (F_PL_INTR_SGE_ERR | F_PL_INTR_SGE_DATA)
+#define SGE_INT_FATAL (F_RESPQ_OVERFLOW | F_PACKET_TOO_BIG | F_PACKET_MISMATCH)
+#define SGE_INT_ENABLE (F_RESPQ_EXHAUSTED | F_RESPQ_OVERFLOW | \
+ F_FL_EXHAUSTED | F_PACKET_TOO_BIG | F_PACKET_MISMATCH)
+
+/*
+ * Disable SGE Interrupts
+ */
+void t1_sge_intr_disable(struct sge *sge)
+{
+ u32 val = readl(sge->adapter->regs + A_PL_ENABLE);
+
+ writel(val & ~SGE_PL_INTR_MASK, sge->adapter->regs + A_PL_ENABLE);
+ writel(0, sge->adapter->regs + A_SG_INT_ENABLE);
+}
+
+/*
+ * Enable SGE interrupts.
+ */
+void t1_sge_intr_enable(struct sge *sge)
+{
+ u32 en = SGE_INT_ENABLE;
+ u32 val = readl(sge->adapter->regs + A_PL_ENABLE);
+
+ if (sge->adapter->flags & TSO_CAPABLE)
+ en &= ~F_PACKET_TOO_BIG;
+ writel(en, sge->adapter->regs + A_SG_INT_ENABLE);
+ writel(val | SGE_PL_INTR_MASK, sge->adapter->regs + A_PL_ENABLE);
+}
+
+/*
+ * Clear SGE interrupts.
+ */
+void t1_sge_intr_clear(struct sge *sge)
+{
+ writel(SGE_PL_INTR_MASK, sge->adapter->regs + A_PL_CAUSE);
+ writel(0xffffffff, sge->adapter->regs + A_SG_INT_CAUSE);
+}
+
+/*
+ * SGE 'Error' interrupt handler
+ */
+int t1_sge_intr_error_handler(struct sge *sge)
+{
+ struct adapter *adapter = sge->adapter;
+ u32 cause = readl(adapter->regs + A_SG_INT_CAUSE);
+
+ if (adapter->flags & TSO_CAPABLE)
+ cause &= ~F_PACKET_TOO_BIG;
+ if (cause & F_RESPQ_EXHAUSTED)
+ sge->stats.respQ_empty++;
+ if (cause & F_RESPQ_OVERFLOW) {
+ sge->stats.respQ_overflow++;
+ CH_ALERT("%s: SGE response queue overflow\n",
+ adapter->name);
+ }
+ if (cause & F_FL_EXHAUSTED) {
+ sge->stats.freelistQ_empty++;
+ freelQs_empty(sge);
+ }
+ if (cause & F_PACKET_TOO_BIG) {
+ sge->stats.pkt_too_big++;
+ CH_ALERT("%s: SGE max packet size exceeded\n",
+ adapter->name);
+ }
+ if (cause & F_PACKET_MISMATCH) {
+ sge->stats.pkt_mismatch++;
+ CH_ALERT("%s: SGE packet mismatch\n", adapter->name);
+ }
+ if (cause & SGE_INT_FATAL)
+ t1_fatal_err(adapter);
+
+ writel(cause, adapter->regs + A_SG_INT_CAUSE);
+ return 0;
+}
+
+const struct sge_intr_counts *t1_sge_get_intr_counts(struct sge *sge)
+{
+ return &sge->stats;
+}
+
+const struct sge_port_stats *t1_sge_get_port_stats(struct sge *sge, int port)
+{
+ return &sge->port_stats[port];
+}
+
+/**
+ * recycle_fl_buf - recycle a free list buffer
+ * @fl: the free list
+ * @idx: index of buffer to recycle
+ *
+ * Recycles the specified buffer on the given free list by adding it at
+ * the next available slot on the list.
+ */
+static void recycle_fl_buf(struct freelQ *fl, int idx)
+{
+ struct freelQ_e *from = &fl->entries[idx];
+ struct freelQ_e *to = &fl->entries[fl->pidx];
+
+ fl->centries[fl->pidx] = fl->centries[idx];
+ to->addr_lo = from->addr_lo;
+ to->addr_hi = from->addr_hi;
+ to->len_gen = G_CMD_LEN(from->len_gen) | V_CMD_GEN1(fl->genbit);
+ wmb();
+ to->gen2 = V_CMD_GEN2(fl->genbit);
+ fl->credits++;
+
+ if (++fl->pidx == fl->size) {
+ fl->pidx = 0;
+ fl->genbit ^= 1;
+ }
+}
+
+/**
+ * get_packet - return the next ingress packet buffer
+ * @pdev: the PCI device that received the packet
+ * @fl: the SGE free list holding the packet
+ * @len: the actual packet length, excluding any SGE padding
+ * @dma_pad: padding at beginning of buffer left by SGE DMA
+ * @skb_pad: padding to be used if the packet is copied
+ * @copy_thres: length threshold under which a packet should be copied
+ * @drop_thres: # of remaining buffers before we start dropping packets
+ *
+ * Get the next packet from a free list and complete setup of the
+ * sk_buff. If the packet is small we make a copy and recycle the
+ * original buffer, otherwise we use the original buffer itself. If a
+ * positive drop threshold is supplied packets are dropped and their
+ * buffers recycled if (a) the number of remaining buffers is under the
+ * threshold and the packet is too big to copy, or (b) the packet should
+ * be copied but there is no memory for the copy.
+ */
+static inline struct sk_buff *get_packet(struct pci_dev *pdev,
+ struct freelQ *fl, unsigned int len,
+ int dma_pad, int skb_pad,
+ unsigned int copy_thres,
+ unsigned int drop_thres)
+{
+ struct sk_buff *skb;
+ struct freelQ_ce *ce = &fl->centries[fl->cidx];
+
+ if (len < copy_thres) {
+ skb = alloc_skb(len + skb_pad, GFP_ATOMIC);
+ if (likely(skb != NULL)) {
+ skb_reserve(skb, skb_pad);
+ skb_put(skb, len);
+ pci_dma_sync_single_for_cpu(pdev,
+ pci_unmap_addr(ce, dma_addr),
+ pci_unmap_len(ce, dma_len),
+ PCI_DMA_FROMDEVICE);
+ memcpy(skb->data, ce->skb->data + dma_pad, len);
+ pci_dma_sync_single_for_device(pdev,
+ pci_unmap_addr(ce, dma_addr),
+ pci_unmap_len(ce, dma_len),
+ PCI_DMA_FROMDEVICE);
+ } else if (!drop_thres)
+ goto use_orig_buf;
+
+ recycle_fl_buf(fl, fl->cidx);
+ return skb;
+ }
+
+ if (fl->credits < drop_thres) {
+ recycle_fl_buf(fl, fl->cidx);
+ return NULL;
+ }
+
+use_orig_buf:
+ pci_unmap_single(pdev, pci_unmap_addr(ce, dma_addr),
+ pci_unmap_len(ce, dma_len), PCI_DMA_FROMDEVICE);
+ skb = ce->skb;
+ skb_reserve(skb, dma_pad);
+ skb_put(skb, len);
+ return skb;
+}
+
+/**
+ * unexpected_offload - handle an unexpected offload packet
+ * @adapter: the adapter
+ * @fl: the free list that received the packet
+ *
+ * Called when we receive an unexpected offload packet (e.g., the TOE
+ * function is disabled or the card is a NIC). Prints a message and
+ * recycles the buffer.
+ */
+static void unexpected_offload(struct adapter *adapter, struct freelQ *fl)
+{
+ struct freelQ_ce *ce = &fl->centries[fl->cidx];
+ struct sk_buff *skb = ce->skb;
+
+ pci_dma_sync_single_for_cpu(adapter->pdev, pci_unmap_addr(ce, dma_addr),
+ pci_unmap_len(ce, dma_len), PCI_DMA_FROMDEVICE);
+ CH_ERR("%s: unexpected offload packet, cmd %u\n",
+ adapter->name, *skb->data);
+ recycle_fl_buf(fl, fl->cidx);
+}
+
+/*
+ * Write the command descriptors to transmit the given skb starting at
+ * descriptor pidx with the given generation.
+ */
+static inline void write_tx_descs(struct adapter *adapter, struct sk_buff *skb,
+ unsigned int pidx, unsigned int gen,
+ struct cmdQ *q)
+{
+ dma_addr_t mapping;
+ struct cmdQ_e *e, *e1;
+ struct cmdQ_ce *ce;
+ unsigned int i, flags, nfrags = skb_shinfo(skb)->nr_frags;
+
+ mapping = pci_map_single(adapter->pdev, skb->data,
+ skb->len - skb->data_len, PCI_DMA_TODEVICE);
+ ce = &q->centries[pidx];
+ ce->skb = NULL;
+ pci_unmap_addr_set(ce, dma_addr, mapping);
+ pci_unmap_len_set(ce, dma_len, skb->len - skb->data_len);
+
+ flags = F_CMD_DATAVALID | F_CMD_SOP | V_CMD_EOP(nfrags == 0) |
+ V_CMD_GEN2(gen);
+ e = &q->entries[pidx];
+ e->addr_lo = (u32)mapping;
+ e->addr_hi = (u64)mapping >> 32;
+ e->len_gen = V_CMD_LEN(skb->len - skb->data_len) | V_CMD_GEN1(gen);
+ for (e1 = e, i = 0; nfrags--; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ ce++;
+ e1++;
+ if (++pidx == q->size) {
+ pidx = 0;
+ gen ^= 1;
+ ce = q->centries;
+ e1 = q->entries;
+ }
+
+ mapping = pci_map_page(adapter->pdev, frag->page,
+ frag->page_offset, frag->size,
+ PCI_DMA_TODEVICE);
+ ce->skb = NULL;
+ pci_unmap_addr_set(ce, dma_addr, mapping);
+ pci_unmap_len_set(ce, dma_len, frag->size);
+
+ e1->addr_lo = (u32)mapping;
+ e1->addr_hi = (u64)mapping >> 32;
+ e1->len_gen = V_CMD_LEN(frag->size) | V_CMD_GEN1(gen);
+ e1->flags = F_CMD_DATAVALID | V_CMD_EOP(nfrags == 0) |
+ V_CMD_GEN2(gen);
+ }
+
+ ce->skb = skb;
+ wmb();
+ e->flags = flags;
+}
+
+/*
+ * Clean up completed Tx buffers.
+ */
+static inline void reclaim_completed_tx(struct sge *sge, struct cmdQ *q)
+{
+ unsigned int reclaim = q->processed - q->cleaned;
+
+ if (reclaim) {
+ free_cmdQ_buffers(sge, q, reclaim);
+ q->cleaned += reclaim;
+ }
+}
+
+#ifndef SET_ETHTOOL_OPS
+# define __netif_rx_complete(dev) netif_rx_complete(dev)
+#endif
+
+/*
+ * We cannot use the standard netif_rx_schedule_prep() because we have multiple
+ * ports plus the TOE all multiplexing onto a single response queue, therefore
+ * accepting new responses cannot depend on the state of any particular port.
+ * So define our own equivalent that omits the netif_running() test.
+ */
+static inline int napi_schedule_prep(struct net_device *dev)
+{
+ return !test_and_set_bit(__LINK_STATE_RX_SCHED, &dev->state);
+}
+
+
+/**
+ * sge_rx - process an ingress ethernet packet
+ * @sge: the sge structure
+ * @fl: the free list that contains the packet buffer
+ * @len: the packet length
+ *
+ * Process an ingress ethernet pakcet and deliver it to the stack.
+ */
+static int sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len)
+{
+ struct sk_buff *skb;
+ struct cpl_rx_pkt *p;
+ struct adapter *adapter = sge->adapter;
+
+ sge->stats.ethernet_pkts++;
+ skb = get_packet(adapter->pdev, fl, len - sge->rx_pkt_pad,
+ sge->rx_pkt_pad, 2, SGE_RX_COPY_THRES,
+ SGE_RX_DROP_THRES);
+ if (!skb) {
+ sge->port_stats[0].rx_drops++; /* charge only port 0 for now */
+ return 0;
+ }
+
+ p = (struct cpl_rx_pkt *)skb->data;
+ skb_pull(skb, sizeof(*p));
+ skb->dev = adapter->port[p->iff].dev;
+ skb->dev->last_rx = jiffies;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+ if ((adapter->flags & RX_CSUM_ENABLED) && p->csum == 0xffff &&
+ skb->protocol == htons(ETH_P_IP) &&
+ (skb->data[9] == IPPROTO_TCP || skb->data[9] == IPPROTO_UDP)) {
+ sge->port_stats[p->iff].rx_cso_good++;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ } else
+ skb->ip_summed = CHECKSUM_NONE;
+
+ if (unlikely(adapter->vlan_grp && p->vlan_valid)) {
+ sge->port_stats[p->iff].vlan_xtract++;
+ if (adapter->params.sge.polling)
+ vlan_hwaccel_receive_skb(skb, adapter->vlan_grp,
+ ntohs(p->vlan));
+ else
+ vlan_hwaccel_rx(skb, adapter->vlan_grp,
+ ntohs(p->vlan));
+ } else if (adapter->params.sge.polling)
+ netif_receive_skb(skb);
+ else
+ netif_rx(skb);
+ return 0;
+}
+
+/*
+ * Returns true if a command queue has enough available descriptors that
+ * we can resume Tx operation after temporarily disabling its packet queue.
+ */
+static inline int enough_free_Tx_descs(const struct cmdQ *q)
+{
+ unsigned int r = q->processed - q->cleaned;
+
+ return q->in_use - r < (q->size >> 1);
+}
+
+/*
+ * Called when sufficient space has become available in the SGE command queues
+ * after the Tx packet schedulers have been suspended to restart the Tx path.
+ */
+static void restart_tx_queues(struct sge *sge)
+{
+ struct adapter *adap = sge->adapter;
+
+ if (enough_free_Tx_descs(&sge->cmdQ[0])) {
+ int i;
+
+ for_each_port(adap, i) {
+ struct net_device *nd = adap->port[i].dev;
+
+ if (test_and_clear_bit(nd->if_port,
+ &sge->stopped_tx_queues) &&
+ netif_running(nd)) {
+ sge->stats.cmdQ_restarted[3]++;
+ netif_wake_queue(nd);
+ }
+ }
+ }
+}
+
+/*
+ * update_tx_info is called from the interrupt handler/NAPI to return cmdQ0
+ * information.
+ */
+static unsigned int update_tx_info(struct adapter *adapter,
+ unsigned int flags,
+ unsigned int pr0)
+{
+ struct sge *sge = adapter->sge;
+ struct cmdQ *cmdq = &sge->cmdQ[0];
+
+ cmdq->processed += pr0;
+
+ if (flags & F_CMDQ0_ENABLE) {
+ clear_bit(CMDQ_STAT_RUNNING, &cmdq->status);
+
+ if (cmdq->cleaned + cmdq->in_use != cmdq->processed &&
+ !test_and_set_bit(CMDQ_STAT_LAST_PKT_DB, &cmdq->status)) {
+ set_bit(CMDQ_STAT_RUNNING, &cmdq->status);
+ writel(F_CMDQ0_ENABLE, adapter->regs + A_SG_DOORBELL);
+ }
+ flags &= ~F_CMDQ0_ENABLE;
+ }
+
+ if (unlikely(sge->stopped_tx_queues != 0))
+ restart_tx_queues(sge);
+
+ return flags;
+}
+
+/*
+ * Process SGE responses, up to the supplied budget. Returns the number of
+ * responses processed. A negative budget is effectively unlimited.
+ */
+static int process_responses(struct adapter *adapter, int budget)
+{
+ struct sge *sge = adapter->sge;
+ struct respQ *q = &sge->respQ;
+ struct respQ_e *e = &q->entries[q->cidx];
+ int budget_left = budget;
+ unsigned int flags = 0;
+ unsigned int cmdq_processed[SGE_CMDQ_N] = {0, 0};
+
+
+ while (likely(budget_left && e->GenerationBit == q->genbit)) {
+ flags |= e->Qsleeping;
+
+ cmdq_processed[0] += e->Cmdq0CreditReturn;
+ cmdq_processed[1] += e->Cmdq1CreditReturn;
+
+ /* We batch updates to the TX side to avoid cacheline
+ * ping-pong of TX state information on MP where the sender
+ * might run on a different CPU than this function...
+ */
+ if (unlikely(flags & F_CMDQ0_ENABLE || cmdq_processed[0] > 64)) {
+ flags = update_tx_info(adapter, flags, cmdq_processed[0]);
+ cmdq_processed[0] = 0;
+ }
+ if (unlikely(cmdq_processed[1] > 16)) {
+ sge->cmdQ[1].processed += cmdq_processed[1];
+ cmdq_processed[1] = 0;
+ }
+ if (likely(e->DataValid)) {
+ struct freelQ *fl = &sge->freelQ[e->FreelistQid];
+
+ if (unlikely(!e->Sop || !e->Eop))
+ BUG();
+ if (unlikely(e->Offload))
+ unexpected_offload(adapter, fl);
+ else
+ sge_rx(sge, fl, e->BufferLength);
+
+ /*
+ * Note: this depends on each packet consuming a
+ * single free-list buffer; cf. the BUG above.
+ */
+ if (++fl->cidx == fl->size)
+ fl->cidx = 0;
+ if (unlikely(--fl->credits <
+ fl->size - SGE_FREEL_REFILL_THRESH))
+ refill_free_list(sge, fl);
+ } else
+ sge->stats.pure_rsps++;
+
+ e++;
+ if (unlikely(++q->cidx == q->size)) {
+ q->cidx = 0;
+ q->genbit ^= 1;
+ e = q->entries;
+ }
+ prefetch(e);
+
+ if (++q->credits > SGE_RESPQ_REPLENISH_THRES) {
+ writel(q->credits, adapter->regs + A_SG_RSPQUEUECREDIT);
+ q->credits = 0;
+ }
+ --budget_left;
+ }
+
+ flags = update_tx_info(adapter, flags, cmdq_processed[0]);
+ sge->cmdQ[1].processed += cmdq_processed[1];
+
+ budget -= budget_left;
+ return budget;
+}
+
+/*
+ * A simpler version of process_responses() that handles only pure (i.e.,
+ * non data-carrying) responses. Such respones are too light-weight to justify
+ * calling a softirq when using NAPI, so we handle them specially in hard
+ * interrupt context. The function is called with a pointer to a response,
+ * which the caller must ensure is a valid pure response. Returns 1 if it
+ * encounters a valid data-carrying response, 0 otherwise.
+ */
+static int process_pure_responses(struct adapter *adapter, struct respQ_e *e)
+{
+ struct sge *sge = adapter->sge;
+ struct respQ *q = &sge->respQ;
+ unsigned int flags = 0;
+ unsigned int cmdq_processed[SGE_CMDQ_N] = {0, 0};
+
+ do {
+ flags |= e->Qsleeping;
+
+ cmdq_processed[0] += e->Cmdq0CreditReturn;
+ cmdq_processed[1] += e->Cmdq1CreditReturn;
+
+ e++;
+ if (unlikely(++q->cidx == q->size)) {
+ q->cidx = 0;
+ q->genbit ^= 1;
+ e = q->entries;
+ }
+ prefetch(e);
+
+ if (++q->credits > SGE_RESPQ_REPLENISH_THRES) {
+ writel(q->credits, adapter->regs + A_SG_RSPQUEUECREDIT);
+ q->credits = 0;
+ }
+ sge->stats.pure_rsps++;
+ } while (e->GenerationBit == q->genbit && !e->DataValid);
+
+ flags = update_tx_info(adapter, flags, cmdq_processed[0]);
+ sge->cmdQ[1].processed += cmdq_processed[1];
+
+ return e->GenerationBit == q->genbit;
+}
+
+/*
+ * Handler for new data events when using NAPI. This does not need any locking
+ * or protection from interrupts as data interrupts are off at this point and
+ * other adapter interrupts do not interfere.
+ */
+static int t1_poll(struct net_device *dev, int *budget)
+{
+ struct adapter *adapter = dev->priv;
+ int effective_budget = min(*budget, dev->quota);
+
+ int work_done = process_responses(adapter, effective_budget);
+ *budget -= work_done;
+ dev->quota -= work_done;
+
+ if (work_done >= effective_budget)
+ return 1;
+
+ __netif_rx_complete(dev);
+
+ /*
+ * Because we don't atomically flush the following write it is
+ * possible that in very rare cases it can reach the device in a way
+ * that races with a new response being written plus an error interrupt
+ * causing the NAPI interrupt handler below to return unhandled status
+ * to the OS. To protect against this would require flushing the write
+ * and doing both the write and the flush with interrupts off. Way too
+ * expensive and unjustifiable given the rarity of the race.
+ */
+ writel(adapter->sge->respQ.cidx, adapter->regs + A_SG_SLEEPING);
+ return 0;
+}
+
+/*
+ * Returns true if the device is already scheduled for polling.
+ */
+static inline int napi_is_scheduled(struct net_device *dev)
+{
+ return test_bit(__LINK_STATE_RX_SCHED, &dev->state);
+}
+
+/*
+ * NAPI version of the main interrupt handler.
+ */
+static irqreturn_t t1_interrupt_napi(int irq, void *data, struct pt_regs *regs)
+{
+ int handled;
+ struct adapter *adapter = data;
+ struct sge *sge = adapter->sge;
+ struct respQ *q = &adapter->sge->respQ;
+
+ /*
+ * Clear the SGE_DATA interrupt first thing. Normally the NAPI
+ * handler has control of the response queue and the interrupt handler
+ * can look at the queue reliably only once it knows NAPI is off.
+ * We can't wait that long to clear the SGE_DATA interrupt because we
+ * could race with t1_poll rearming the SGE interrupt, so we need to
+ * clear the interrupt speculatively and really early on.
+ */
+ writel(F_PL_INTR_SGE_DATA, adapter->regs + A_PL_CAUSE);
+
+ spin_lock(&adapter->async_lock);
+ if (!napi_is_scheduled(sge->netdev)) {
+ struct respQ_e *e = &q->entries[q->cidx];
+
+ if (e->GenerationBit == q->genbit) {
+ if (e->DataValid ||
+ process_pure_responses(adapter, e)) {
+ if (likely(napi_schedule_prep(sge->netdev)))
+ __netif_rx_schedule(sge->netdev);
+ else
+ printk(KERN_CRIT
+ "NAPI schedule failure!\n");
+ } else
+ writel(q->cidx, adapter->regs + A_SG_SLEEPING);
+ handled = 1;
+ goto unlock;
+ } else
+ writel(q->cidx, adapter->regs + A_SG_SLEEPING);
+ } else
+ if (readl(adapter->regs + A_PL_CAUSE) & F_PL_INTR_SGE_DATA)
+ printk(KERN_ERR "data interrupt while NAPI running\n");
+
+ handled = t1_slow_intr_handler(adapter);
+ if (!handled)
+ sge->stats.unhandled_irqs++;
+ unlock:
+ spin_unlock(&adapter->async_lock);
+ return IRQ_RETVAL(handled != 0);
+}
+
+/*
+ * Main interrupt handler, optimized assuming that we took a 'DATA'
+ * interrupt.
+ *
+ * 1. Clear the interrupt
+ * 2. Loop while we find valid descriptors and process them; accumulate
+ * information that can be processed after the loop
+ * 3. Tell the SGE at which index we stopped processing descriptors
+ * 4. Bookkeeping; free TX buffers, ring doorbell if there are any
+ * outstanding TX buffers waiting, replenish RX buffers, potentially
+ * reenable upper layers if they were turned off due to lack of TX
+ * resources which are available again.
+ * 5. If we took an interrupt, but no valid respQ descriptors was found we
+ * let the slow_intr_handler run and do error handling.
+ */
+static irqreturn_t t1_interrupt(int irq, void *cookie, struct pt_regs *regs)
+{
+ int work_done;
+ struct respQ_e *e;
+ struct adapter *adapter = cookie;
+ struct respQ *Q = &adapter->sge->respQ;
+
+ spin_lock(&adapter->async_lock);
+ e = &Q->entries[Q->cidx];
+ prefetch(e);
+
+ writel(F_PL_INTR_SGE_DATA, adapter->regs + A_PL_CAUSE);
+
+ if (likely(e->GenerationBit == Q->genbit))
+ work_done = process_responses(adapter, -1);
+ else
+ work_done = t1_slow_intr_handler(adapter);
+
+ /*
+ * The unconditional clearing of the PL_CAUSE above may have raced
+ * with DMA completion and the corresponding generation of a response
+ * to cause us to miss the resulting data interrupt. The next write
+ * is also unconditional to recover the missed interrupt and render
+ * this race harmless.
+ */
+ writel(Q->cidx, adapter->regs + A_SG_SLEEPING);
+
+ if (!work_done)
+ adapter->sge->stats.unhandled_irqs++;
+ spin_unlock(&adapter->async_lock);
+ return IRQ_RETVAL(work_done != 0);
+}
+
+intr_handler_t t1_select_intr_handler(adapter_t *adapter)
+{
+ return adapter->params.sge.polling ? t1_interrupt_napi : t1_interrupt;
+}
+
+/*
+ * Enqueues the sk_buff onto the cmdQ[qid] and has hardware fetch it.
+ *
+ * The code figures out how many entries the sk_buff will require in the
+ * cmdQ and updates the cmdQ data structure with the state once the enqueue
+ * has complete. Then, it doesn't access the global structure anymore, but
+ * uses the corresponding fields on the stack. In conjuction with a spinlock
+ * around that code, we can make the function reentrant without holding the
+ * lock when we actually enqueue (which might be expensive, especially on
+ * architectures with IO MMUs).
+ *
+ * This runs with softirqs disabled.
+ */
+unsigned int t1_sge_tx(struct sk_buff *skb, struct adapter *adapter,
+ unsigned int qid, struct net_device *dev)
+{
+ struct sge *sge = adapter->sge;
+ struct cmdQ *q = &sge->cmdQ[qid];
+ unsigned int credits, pidx, genbit, count;
+
+ spin_lock(&q->lock);
+ reclaim_completed_tx(sge, q);
+
+ pidx = q->pidx;
+ credits = q->size - q->in_use;
+ count = 1 + skb_shinfo(skb)->nr_frags;
+
+ { /* Ethernet packet */
+ if (unlikely(credits < count)) {
+ netif_stop_queue(dev);
+ set_bit(dev->if_port, &sge->stopped_tx_queues);
+ sge->stats.cmdQ_full[3]++;
+ spin_unlock(&q->lock);
+ CH_ERR("%s: Tx ring full while queue awake!\n",
+ adapter->name);
+ return 1;
+ }
+ if (unlikely(credits - count < q->stop_thres)) {
+ sge->stats.cmdQ_full[3]++;
+ netif_stop_queue(dev);
+ set_bit(dev->if_port, &sge->stopped_tx_queues);
+ }
+ }
+ q->in_use += count;
+ genbit = q->genbit;
+ q->pidx += count;
+ if (q->pidx >= q->size) {
+ q->pidx -= q->size;
+ q->genbit ^= 1;
+ }
+ spin_unlock(&q->lock);
+
+ write_tx_descs(adapter, skb, pidx, genbit, q);
+
+ /*
+ * We always ring the doorbell for cmdQ1. For cmdQ0, we only ring
+ * the doorbell if the Q is asleep. There is a natural race, where
+ * the hardware is going to sleep just after we checked, however,
+ * then the interrupt handler will detect the outstanding TX packet
+ * and ring the doorbell for us.
+ */
+ if (qid)
+ doorbell_pio(adapter, F_CMDQ1_ENABLE);
+ else {
+ clear_bit(CMDQ_STAT_LAST_PKT_DB, &q->status);
+ if (test_and_set_bit(CMDQ_STAT_RUNNING, &q->status) == 0) {
+ set_bit(CMDQ_STAT_LAST_PKT_DB, &q->status);
+ writel(F_CMDQ0_ENABLE, adapter->regs + A_SG_DOORBELL);
+ }
+ }
+ return 0;
+}
+
+#define MK_ETH_TYPE_MSS(type, mss) (((mss) & 0x3FFF) | ((type) << 14))
+
+/*
+ * eth_hdr_len - return the length of an Ethernet header
+ * @data: pointer to the start of the Ethernet header
+ *
+ * Returns the length of an Ethernet header, including optional VLAN tag.
+ */
+static inline int eth_hdr_len(const void *data)
+{
+ const struct ethhdr *e = data;
+
+ return e->h_proto == htons(ETH_P_8021Q) ? VLAN_ETH_HLEN : ETH_HLEN;
+}
+
+/*
+ * Adds the CPL header to the sk_buff and passes it to t1_sge_tx.
+ */
+int t1_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct adapter *adapter = dev->priv;
+ struct sge_port_stats *st = &adapter->sge->port_stats[dev->if_port];
+ struct sge *sge = adapter->sge;
+ struct cpl_tx_pkt *cpl;
+
+#ifdef NETIF_F_TSO
+ if (skb_shinfo(skb)->tso_size) {
+ int eth_type;
+ struct cpl_tx_pkt_lso *hdr;
+
+ st->tso++;
+
+ eth_type = skb->nh.raw - skb->data == ETH_HLEN ?
+ CPL_ETH_II : CPL_ETH_II_VLAN;
+
+ hdr = (struct cpl_tx_pkt_lso *)skb_push(skb, sizeof(*hdr));
+ hdr->opcode = CPL_TX_PKT_LSO;
+ hdr->ip_csum_dis = hdr->l4_csum_dis = 0;
+ hdr->ip_hdr_words = skb->nh.iph->ihl;
+ hdr->tcp_hdr_words = skb->h.th->doff;
+ hdr->eth_type_mss = htons(MK_ETH_TYPE_MSS(eth_type,
+ skb_shinfo(skb)->tso_size));
+ hdr->len = htonl(skb->len - sizeof(*hdr));
+ cpl = (struct cpl_tx_pkt *)hdr;
+ sge->stats.tx_lso_pkts++;
+ } else
+#endif
+ {
+ /*
+ * Packets shorter than ETH_HLEN can break the MAC, drop them
+ * early. Also, we may get oversized packets because some
+ * parts of the kernel don't handle our unusual hard_header_len
+ * right, drop those too.
+ */
+ if (unlikely(skb->len < ETH_HLEN ||
+ skb->len > dev->mtu + eth_hdr_len(skb->data))) {
+ dev_kfree_skb_any(skb);
+ return NET_XMIT_SUCCESS;
+ }
+
+ /*
+ * We are using a non-standard hard_header_len and some kernel
+ * components, such as pktgen, do not handle it right.
+ * Complain when this happens but try to fix things up.
+ */
+ if (unlikely(skb_headroom(skb) <
+ dev->hard_header_len - ETH_HLEN)) {
+ struct sk_buff *orig_skb = skb;
+
+ if (net_ratelimit())
+ printk(KERN_ERR "%s: inadequate headroom in "
+ "Tx packet\n", dev->name);
+ skb = skb_realloc_headroom(skb, sizeof(*cpl));
+ dev_kfree_skb_any(orig_skb);
+ if (!skb)
+ return -ENOMEM;
+ }
+
+ if (!(adapter->flags & UDP_CSUM_CAPABLE) &&
+ skb->ip_summed == CHECKSUM_HW &&
+ skb->nh.iph->protocol == IPPROTO_UDP)
+ if (unlikely(skb_checksum_help(skb, 0))) {
+ dev_kfree_skb_any(skb);
+ return -ENOMEM;
+ }
+
+ /* Hmmm, assuming to catch the gratious arp... and we'll use
+ * it to flush out stuck espi packets...
+ */
+ if (unlikely(!adapter->sge->espibug_skb)) {
+ if (skb->protocol == htons(ETH_P_ARP) &&
+ skb->nh.arph->ar_op == htons(ARPOP_REQUEST)) {
+ adapter->sge->espibug_skb = skb;
+ /* We want to re-use this skb later. We
+ * simply bump the reference count and it
+ * will not be freed...
+ */
+ skb = skb_get(skb);
+ }
+ }
+
+ cpl = (struct cpl_tx_pkt *)__skb_push(skb, sizeof(*cpl));
+ cpl->opcode = CPL_TX_PKT;
+ cpl->ip_csum_dis = 1; /* SW calculates IP csum */
+ cpl->l4_csum_dis = skb->ip_summed == CHECKSUM_HW ? 0 : 1;
+ /* the length field isn't used so don't bother setting it */
+
+ st->tx_cso += (skb->ip_summed == CHECKSUM_HW);
+ sge->stats.tx_do_cksum += (skb->ip_summed == CHECKSUM_HW);
+ sge->stats.tx_reg_pkts++;
+ }
+ cpl->iff = dev->if_port;
+
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+ if (adapter->vlan_grp && vlan_tx_tag_present(skb)) {
+ cpl->vlan_valid = 1;
+ cpl->vlan = htons(vlan_tx_tag_get(skb));
+ st->vlan_insert++;
+ } else
+#endif
+ cpl->vlan_valid = 0;
+
+ dev->trans_start = jiffies;
+ return t1_sge_tx(skb, adapter, 0, dev);
+}
+
+/*
+ * Callback for the Tx buffer reclaim timer. Runs with softirqs disabled.
+ */
+static void sge_tx_reclaim_cb(unsigned long data)
+{
+ int i;
+ struct sge *sge = (struct sge *)data;
+
+ for (i = 0; i < SGE_CMDQ_N; ++i) {
+ struct cmdQ *q = &sge->cmdQ[i];
+
+ if (!spin_trylock(&q->lock))
+ continue;
+
+ reclaim_completed_tx(sge, q);
+ if (i == 0 && q->in_use) /* flush pending credits */
+ writel(F_CMDQ0_ENABLE,
+ sge->adapter->regs + A_SG_DOORBELL);
+
+ spin_unlock(&q->lock);
+ }
+ mod_timer(&sge->tx_reclaim_timer, jiffies + TX_RECLAIM_PERIOD);
+}
+
+/*
+ * Propagate changes of the SGE coalescing parameters to the HW.
+ */
+int t1_sge_set_coalesce_params(struct sge *sge, struct sge_params *p)
+{
+ sge->netdev->poll = t1_poll;
+ sge->fixed_intrtimer = p->rx_coalesce_usecs *
+ core_ticks_per_usec(sge->adapter);
+ writel(sge->fixed_intrtimer, sge->adapter->regs + A_SG_INTRTIMER);
+ return 0;
+}
+
+/*
+ * Allocates both RX and TX resources and configures the SGE. However,
+ * the hardware is not enabled yet.
+ */
+int t1_sge_configure(struct sge *sge, struct sge_params *p)
+{
+ if (alloc_rx_resources(sge, p))
+ return -ENOMEM;
+ if (alloc_tx_resources(sge, p)) {
+ free_rx_resources(sge);
+ return -ENOMEM;
+ }
+ configure_sge(sge, p);
+
+ /*
+ * Now that we have sized the free lists calculate the payload
+ * capacity of the large buffers. Other parts of the driver use
+ * this to set the max offload coalescing size so that RX packets
+ * do not overflow our large buffers.
+ */
+ p->large_buf_capacity = jumbo_payload_capacity(sge);
+ return 0;
+}
+
+/*
+ * Disables the DMA engine.
+ */
+void t1_sge_stop(struct sge *sge)
+{
+ writel(0, sge->adapter->regs + A_SG_CONTROL);
+ (void) readl(sge->adapter->regs + A_SG_CONTROL); /* flush */
+ if (is_T2(sge->adapter))
+ del_timer_sync(&sge->espibug_timer);
+ del_timer_sync(&sge->tx_reclaim_timer);
+}
+
+/*
+ * Enables the DMA engine.
+ */
+void t1_sge_start(struct sge *sge)
+{
+ refill_free_list(sge, &sge->freelQ[0]);
+ refill_free_list(sge, &sge->freelQ[1]);
+
+ writel(sge->sge_control, sge->adapter->regs + A_SG_CONTROL);
+ doorbell_pio(sge->adapter, F_FL0_ENABLE | F_FL1_ENABLE);
+ (void) readl(sge->adapter->regs + A_SG_CONTROL); /* flush */
+
+ mod_timer(&sge->tx_reclaim_timer, jiffies + TX_RECLAIM_PERIOD);
+
+ if (is_T2(sge->adapter))
+ mod_timer(&sge->espibug_timer, jiffies + sge->espibug_timeout);
+}
+
+/*
+ * Callback for the T2 ESPI 'stuck packet feature' workaorund
+ */
+static void espibug_workaround(void *data)
+{
+ struct adapter *adapter = (struct adapter *)data;
+ struct sge *sge = adapter->sge;
+
+ if (netif_running(adapter->port[0].dev)) {
+ struct sk_buff *skb = sge->espibug_skb;
+
+ u32 seop = t1_espi_get_mon(adapter, 0x930, 0);
+
+ if ((seop & 0xfff0fff) == 0xfff && skb) {
+ if (!skb->cb[0]) {
+ u8 ch_mac_addr[ETH_ALEN] =
+ {0x0, 0x7, 0x43, 0x0, 0x0, 0x0};
+ memcpy(skb->data + sizeof(struct cpl_tx_pkt),
+ ch_mac_addr, ETH_ALEN);
+ memcpy(skb->data + skb->len - 10, ch_mac_addr,
+ ETH_ALEN);
+ skb->cb[0] = 0xff;
+ }
+
+ /* bump the reference count to avoid freeing of the
+ * skb once the DMA has completed.
+ */
+ skb = skb_get(skb);
+ t1_sge_tx(skb, adapter, 0, adapter->port[0].dev);
+ }
+ }
+ mod_timer(&sge->espibug_timer, jiffies + sge->espibug_timeout);
+}
+
+/*
+ * Creates a t1_sge structure and returns suggested resource parameters.
+ */
+struct sge * __devinit t1_sge_create(struct adapter *adapter,
+ struct sge_params *p)
+{
+ struct sge *sge = kmalloc(sizeof(*sge), GFP_KERNEL);
+
+ if (!sge)
+ return NULL;
+ memset(sge, 0, sizeof(*sge));
+
+ sge->adapter = adapter;
+ sge->netdev = adapter->port[0].dev;
+ sge->rx_pkt_pad = t1_is_T1B(adapter) ? 0 : 2;
+ sge->jumbo_fl = t1_is_T1B(adapter) ? 1 : 0;
+
+ init_timer(&sge->tx_reclaim_timer);
+ sge->tx_reclaim_timer.data = (unsigned long)sge;
+ sge->tx_reclaim_timer.function = sge_tx_reclaim_cb;
+
+ if (is_T2(sge->adapter)) {
+ init_timer(&sge->espibug_timer);
+ sge->espibug_timer.function = (void *)&espibug_workaround;
+ sge->espibug_timer.data = (unsigned long)sge->adapter;
+ sge->espibug_timeout = 1;
+ }
+
+
+ p->cmdQ_size[0] = SGE_CMDQ0_E_N;
+ p->cmdQ_size[1] = SGE_CMDQ1_E_N;
+ p->freelQ_size[!sge->jumbo_fl] = SGE_FREEL_SIZE;
+ p->freelQ_size[sge->jumbo_fl] = SGE_JUMBO_FREEL_SIZE;
+ p->rx_coalesce_usecs = 50;
+ p->coalesce_enable = 0;
+ p->sample_interval_usecs = 0;
+ p->polling = 0;
+
+ return sge;
+}
diff --git a/drivers/net/chelsio/sge.h b/drivers/net/chelsio/sge.h
new file mode 100644
index 000000000000..434b25586851
--- /dev/null
+++ b/drivers/net/chelsio/sge.h
@@ -0,0 +1,105 @@
+/*****************************************************************************
+ * *
+ * File: sge.h *
+ * $Revision: 1.11 $ *
+ * $Date: 2005/06/21 22:10:55 $ *
+ * Description: *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#ifndef _CXGB_SGE_H_
+#define _CXGB_SGE_H_
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <asm/byteorder.h>
+
+#ifndef IRQ_RETVAL
+#define IRQ_RETVAL(x)
+typedef void irqreturn_t;
+#endif
+
+typedef irqreturn_t (*intr_handler_t)(int, void *, struct pt_regs *);
+
+struct sge_intr_counts {
+ unsigned int respQ_empty; /* # times respQ empty */
+ unsigned int respQ_overflow; /* # respQ overflow (fatal) */
+ unsigned int freelistQ_empty; /* # times freelist empty */
+ unsigned int pkt_too_big; /* packet too large (fatal) */
+ unsigned int pkt_mismatch;
+ unsigned int cmdQ_full[3]; /* not HW IRQ, host cmdQ[] full */
+ unsigned int cmdQ_restarted[3];/* # of times cmdQ X was restarted */
+ unsigned int ethernet_pkts; /* # of Ethernet packets received */
+ unsigned int offload_pkts; /* # of offload packets received */
+ unsigned int offload_bundles; /* # of offload pkt bundles delivered */
+ unsigned int pure_rsps; /* # of non-payload responses */
+ unsigned int unhandled_irqs; /* # of unhandled interrupts */
+ unsigned int tx_ipfrags;
+ unsigned int tx_reg_pkts;
+ unsigned int tx_lso_pkts;
+ unsigned int tx_do_cksum;
+};
+
+struct sge_port_stats {
+ unsigned long rx_cso_good; /* # of successful RX csum offloads */
+ unsigned long tx_cso; /* # of TX checksum offloads */
+ unsigned long vlan_xtract; /* # of VLAN tag extractions */
+ unsigned long vlan_insert; /* # of VLAN tag extractions */
+ unsigned long tso; /* # of TSO requests */
+ unsigned long rx_drops; /* # of packets dropped due to no mem */
+};
+
+struct sk_buff;
+struct net_device;
+struct adapter;
+struct sge_params;
+struct sge;
+
+struct sge *t1_sge_create(struct adapter *, struct sge_params *);
+int t1_sge_configure(struct sge *, struct sge_params *);
+int t1_sge_set_coalesce_params(struct sge *, struct sge_params *);
+void t1_sge_destroy(struct sge *);
+intr_handler_t t1_select_intr_handler(adapter_t *adapter);
+unsigned int t1_sge_tx(struct sk_buff *skb, struct adapter *adapter,
+ unsigned int qid, struct net_device *netdev);
+int t1_start_xmit(struct sk_buff *skb, struct net_device *dev);
+void t1_set_vlan_accel(struct adapter *adapter, int on_off);
+void t1_sge_start(struct sge *);
+void t1_sge_stop(struct sge *);
+int t1_sge_intr_error_handler(struct sge *);
+void t1_sge_intr_enable(struct sge *);
+void t1_sge_intr_disable(struct sge *);
+void t1_sge_intr_clear(struct sge *);
+const struct sge_intr_counts *t1_sge_get_intr_counts(struct sge *sge);
+const struct sge_port_stats *t1_sge_get_port_stats(struct sge *sge, int port);
+
+#endif /* _CXGB_SGE_H_ */
diff --git a/drivers/net/chelsio/subr.c b/drivers/net/chelsio/subr.c
new file mode 100644
index 000000000000..1ebb5d149aef
--- /dev/null
+++ b/drivers/net/chelsio/subr.c
@@ -0,0 +1,812 @@
+/*****************************************************************************
+ * *
+ * File: subr.c *
+ * $Revision: 1.27 $ *
+ * $Date: 2005/06/22 01:08:36 $ *
+ * Description: *
+ * Various subroutines (intr,pio,etc.) used by Chelsio 10G Ethernet driver. *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: Dimitrios Michailidis <dm@chelsio.com> *
+ * Tina Yang <tainay@chelsio.com> *
+ * Felix Marti <felix@chelsio.com> *
+ * Scott Bardone <sbardone@chelsio.com> *
+ * Kurt Ottaway <kottaway@chelsio.com> *
+ * Frank DiMambro <frank@chelsio.com> *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#include "common.h"
+#include "elmer0.h"
+#include "regs.h"
+#include "gmac.h"
+#include "cphy.h"
+#include "sge.h"
+#include "espi.h"
+
+/**
+ * t1_wait_op_done - wait until an operation is completed
+ * @adapter: the adapter performing the operation
+ * @reg: the register to check for completion
+ * @mask: a single-bit field within @reg that indicates completion
+ * @polarity: the value of the field when the operation is completed
+ * @attempts: number of check iterations
+ * @delay: delay in usecs between iterations
+ *
+ * Wait until an operation is completed by checking a bit in a register
+ * up to @attempts times. Returns %0 if the operation completes and %1
+ * otherwise.
+ */
+static int t1_wait_op_done(adapter_t *adapter, int reg, u32 mask, int polarity,
+ int attempts, int delay)
+{
+ while (1) {
+ u32 val = readl(adapter->regs + reg) & mask;
+
+ if (!!val == polarity)
+ return 0;
+ if (--attempts == 0)
+ return 1;
+ if (delay)
+ udelay(delay);
+ }
+}
+
+#define TPI_ATTEMPTS 50
+
+/*
+ * Write a register over the TPI interface (unlocked and locked versions).
+ */
+static int __t1_tpi_write(adapter_t *adapter, u32 addr, u32 value)
+{
+ int tpi_busy;
+
+ writel(addr, adapter->regs + A_TPI_ADDR);
+ writel(value, adapter->regs + A_TPI_WR_DATA);
+ writel(F_TPIWR, adapter->regs + A_TPI_CSR);
+
+ tpi_busy = t1_wait_op_done(adapter, A_TPI_CSR, F_TPIRDY, 1,
+ TPI_ATTEMPTS, 3);
+ if (tpi_busy)
+ CH_ALERT("%s: TPI write to 0x%x failed\n",
+ adapter->name, addr);
+ return tpi_busy;
+}
+
+int t1_tpi_write(adapter_t *adapter, u32 addr, u32 value)
+{
+ int ret;
+
+ spin_lock(&(adapter)->tpi_lock);
+ ret = __t1_tpi_write(adapter, addr, value);
+ spin_unlock(&(adapter)->tpi_lock);
+ return ret;
+}
+
+/*
+ * Read a register over the TPI interface (unlocked and locked versions).
+ */
+static int __t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp)
+{
+ int tpi_busy;
+
+ writel(addr, adapter->regs + A_TPI_ADDR);
+ writel(0, adapter->regs + A_TPI_CSR);
+
+ tpi_busy = t1_wait_op_done(adapter, A_TPI_CSR, F_TPIRDY, 1,
+ TPI_ATTEMPTS, 3);
+ if (tpi_busy)
+ CH_ALERT("%s: TPI read from 0x%x failed\n",
+ adapter->name, addr);
+ else
+ *valp = readl(adapter->regs + A_TPI_RD_DATA);
+ return tpi_busy;
+}
+
+int t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp)
+{
+ int ret;
+
+ spin_lock(&(adapter)->tpi_lock);
+ ret = __t1_tpi_read(adapter, addr, valp);
+ spin_unlock(&(adapter)->tpi_lock);
+ return ret;
+}
+
+/*
+ * Called when a port's link settings change to propagate the new values to the
+ * associated PHY and MAC. After performing the common tasks it invokes an
+ * OS-specific handler.
+ */
+/* static */ void link_changed(adapter_t *adapter, int port_id)
+{
+ int link_ok, speed, duplex, fc;
+ struct cphy *phy = adapter->port[port_id].phy;
+ struct link_config *lc = &adapter->port[port_id].link_config;
+
+ phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc);
+
+ lc->speed = speed < 0 ? SPEED_INVALID : speed;
+ lc->duplex = duplex < 0 ? DUPLEX_INVALID : duplex;
+ if (!(lc->requested_fc & PAUSE_AUTONEG))
+ fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);
+
+ if (link_ok && speed >= 0 && lc->autoneg == AUTONEG_ENABLE) {
+ /* Set MAC speed, duplex, and flow control to match PHY. */
+ struct cmac *mac = adapter->port[port_id].mac;
+
+ mac->ops->set_speed_duplex_fc(mac, speed, duplex, fc);
+ lc->fc = (unsigned char)fc;
+ }
+ t1_link_changed(adapter, port_id, link_ok, speed, duplex, fc);
+}
+
+static int t1_pci_intr_handler(adapter_t *adapter)
+{
+ u32 pcix_cause;
+
+ pci_read_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE, &pcix_cause);
+
+ if (pcix_cause) {
+ pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE,
+ pcix_cause);
+ t1_fatal_err(adapter); /* PCI errors are fatal */
+ }
+ return 0;
+}
+
+
+/*
+ * Wait until Elmer's MI1 interface is ready for new operations.
+ */
+static int mi1_wait_until_ready(adapter_t *adapter, int mi1_reg)
+{
+ int attempts = 100, busy;
+
+ do {
+ u32 val;
+
+ __t1_tpi_read(adapter, mi1_reg, &val);
+ busy = val & F_MI1_OP_BUSY;
+ if (busy)
+ udelay(10);
+ } while (busy && --attempts);
+ if (busy)
+ CH_ALERT("%s: MDIO operation timed out\n",
+ adapter->name);
+ return busy;
+}
+
+/*
+ * MI1 MDIO initialization.
+ */
+static void mi1_mdio_init(adapter_t *adapter, const struct board_info *bi)
+{
+ u32 clkdiv = bi->clock_elmer0 / (2 * bi->mdio_mdc) - 1;
+ u32 val = F_MI1_PREAMBLE_ENABLE | V_MI1_MDI_INVERT(bi->mdio_mdiinv) |
+ V_MI1_MDI_ENABLE(bi->mdio_mdien) | V_MI1_CLK_DIV(clkdiv);
+
+ if (!(bi->caps & SUPPORTED_10000baseT_Full))
+ val |= V_MI1_SOF(1);
+ t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_CFG, val);
+}
+
+static int mi1_mdio_ext_read(adapter_t *adapter, int phy_addr, int mmd_addr,
+ int reg_addr, unsigned int *valp)
+{
+ u32 addr = V_MI1_REG_ADDR(mmd_addr) | V_MI1_PHY_ADDR(phy_addr);
+
+ spin_lock(&(adapter)->tpi_lock);
+
+ /* Write the address we want. */
+ __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr);
+ __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, reg_addr);
+ __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP,
+ MI1_OP_INDIRECT_ADDRESS);
+ mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);
+
+ /* Write the operation we want. */
+ __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP, MI1_OP_INDIRECT_READ);
+ mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);
+
+ /* Read the data. */
+ __t1_tpi_read(adapter, A_ELMER0_PORT0_MI1_DATA, valp);
+ spin_unlock(&(adapter)->tpi_lock);
+ return 0;
+}
+
+static int mi1_mdio_ext_write(adapter_t *adapter, int phy_addr, int mmd_addr,
+ int reg_addr, unsigned int val)
+{
+ u32 addr = V_MI1_REG_ADDR(mmd_addr) | V_MI1_PHY_ADDR(phy_addr);
+
+ spin_lock(&(adapter)->tpi_lock);
+
+ /* Write the address we want. */
+ __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr);
+ __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, reg_addr);
+ __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP,
+ MI1_OP_INDIRECT_ADDRESS);
+ mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);
+
+ /* Write the data. */
+ __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, val);
+ __t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP, MI1_OP_INDIRECT_WRITE);
+ mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);
+ spin_unlock(&(adapter)->tpi_lock);
+ return 0;
+}
+
+static struct mdio_ops mi1_mdio_ext_ops = {
+ mi1_mdio_init,
+ mi1_mdio_ext_read,
+ mi1_mdio_ext_write
+};
+
+enum {
+ CH_BRD_N110_1F,
+ CH_BRD_N210_1F,
+};
+
+static struct board_info t1_board[] = {
+
+{ CHBT_BOARD_N110, 1/*ports#*/,
+ SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE /*caps*/, CHBT_TERM_T1,
+ CHBT_MAC_PM3393, CHBT_PHY_88X2010,
+ 125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/,
+ 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
+ 0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops,
+ &t1_mv88x201x_ops, &mi1_mdio_ext_ops,
+ "Chelsio N110 1x10GBaseX NIC" },
+
+{ CHBT_BOARD_N210, 1/*ports#*/,
+ SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE /*caps*/, CHBT_TERM_T2,
+ CHBT_MAC_PM3393, CHBT_PHY_88X2010,
+ 125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/,
+ 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
+ 0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops,
+ &t1_mv88x201x_ops, &mi1_mdio_ext_ops,
+ "Chelsio N210 1x10GBaseX NIC" },
+
+};
+
+struct pci_device_id t1_pci_tbl[] = {
+ CH_DEVICE(7, 0, CH_BRD_N110_1F),
+ CH_DEVICE(10, 1, CH_BRD_N210_1F),
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, t1_pci_tbl);
+
+/*
+ * Return the board_info structure with a given index. Out-of-range indices
+ * return NULL.
+ */
+const struct board_info *t1_get_board_info(unsigned int board_id)
+{
+ return board_id < ARRAY_SIZE(t1_board) ? &t1_board[board_id] : NULL;
+}
+
+struct chelsio_vpd_t {
+ u32 format_version;
+ u8 serial_number[16];
+ u8 mac_base_address[6];
+ u8 pad[2]; /* make multiple-of-4 size requirement explicit */
+};
+
+#define EEPROMSIZE (8 * 1024)
+#define EEPROM_MAX_POLL 4
+
+/*
+ * Read SEEPROM. A zero is written to the flag register when the addres is
+ * written to the Control register. The hardware device will set the flag to a
+ * one when 4B have been transferred to the Data register.
+ */
+int t1_seeprom_read(adapter_t *adapter, u32 addr, u32 *data)
+{
+ int i = EEPROM_MAX_POLL;
+ u16 val;
+
+ if (addr >= EEPROMSIZE || (addr & 3))
+ return -EINVAL;
+
+ pci_write_config_word(adapter->pdev, A_PCICFG_VPD_ADDR, (u16)addr);
+ do {
+ udelay(50);
+ pci_read_config_word(adapter->pdev, A_PCICFG_VPD_ADDR, &val);
+ } while (!(val & F_VPD_OP_FLAG) && --i);
+
+ if (!(val & F_VPD_OP_FLAG)) {
+ CH_ERR("%s: reading EEPROM address 0x%x failed\n",
+ adapter->name, addr);
+ return -EIO;
+ }
+ pci_read_config_dword(adapter->pdev, A_PCICFG_VPD_DATA, data);
+ *data = le32_to_cpu(*data);
+ return 0;
+}
+
+static int t1_eeprom_vpd_get(adapter_t *adapter, struct chelsio_vpd_t *vpd)
+{
+ int addr, ret = 0;
+
+ for (addr = 0; !ret && addr < sizeof(*vpd); addr += sizeof(u32))
+ ret = t1_seeprom_read(adapter, addr,
+ (u32 *)((u8 *)vpd + addr));
+
+ return ret;
+}
+
+/*
+ * Read a port's MAC address from the VPD ROM.
+ */
+static int vpd_macaddress_get(adapter_t *adapter, int index, u8 mac_addr[])
+{
+ struct chelsio_vpd_t vpd;
+
+ if (t1_eeprom_vpd_get(adapter, &vpd))
+ return 1;
+ memcpy(mac_addr, vpd.mac_base_address, 5);
+ mac_addr[5] = vpd.mac_base_address[5] + index;
+ return 0;
+}
+
+/*
+ * Set up the MAC/PHY according to the requested link settings.
+ *
+ * If the PHY can auto-negotiate first decide what to advertise, then
+ * enable/disable auto-negotiation as desired and reset.
+ *
+ * If the PHY does not auto-negotiate we just reset it.
+ *
+ * If auto-negotiation is off set the MAC to the proper speed/duplex/FC,
+ * otherwise do it later based on the outcome of auto-negotiation.
+ */
+int t1_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc)
+{
+ unsigned int fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);
+
+ if (lc->supported & SUPPORTED_Autoneg) {
+ lc->advertising &= ~(ADVERTISED_ASYM_PAUSE | ADVERTISED_PAUSE);
+ if (fc) {
+ lc->advertising |= ADVERTISED_ASYM_PAUSE;
+ if (fc == (PAUSE_RX | PAUSE_TX))
+ lc->advertising |= ADVERTISED_PAUSE;
+ }
+ phy->ops->advertise(phy, lc->advertising);
+
+ if (lc->autoneg == AUTONEG_DISABLE) {
+ lc->speed = lc->requested_speed;
+ lc->duplex = lc->requested_duplex;
+ lc->fc = (unsigned char)fc;
+ mac->ops->set_speed_duplex_fc(mac, lc->speed,
+ lc->duplex, fc);
+ /* Also disables autoneg */
+ phy->ops->set_speed_duplex(phy, lc->speed, lc->duplex);
+ phy->ops->reset(phy, 0);
+ } else
+ phy->ops->autoneg_enable(phy); /* also resets PHY */
+ } else {
+ mac->ops->set_speed_duplex_fc(mac, -1, -1, fc);
+ lc->fc = (unsigned char)fc;
+ phy->ops->reset(phy, 0);
+ }
+ return 0;
+}
+
+/*
+ * External interrupt handler for boards using elmer0.
+ */
+int elmer0_ext_intr_handler(adapter_t *adapter)
+{
+ struct cphy *phy;
+ int phy_cause;
+ u32 cause;
+
+ t1_tpi_read(adapter, A_ELMER0_INT_CAUSE, &cause);
+
+ switch (board_info(adapter)->board) {
+ case CHBT_BOARD_N210:
+ case CHBT_BOARD_N110:
+ if (cause & ELMER0_GP_BIT6) { /* Marvell 88x2010 interrupt */
+ phy = adapter->port[0].phy;
+ phy_cause = phy->ops->interrupt_handler(phy);
+ if (phy_cause & cphy_cause_link_change)
+ link_changed(adapter, 0);
+ }
+ break;
+ }
+ t1_tpi_write(adapter, A_ELMER0_INT_CAUSE, cause);
+ return 0;
+}
+
+/* Enables all interrupts. */
+void t1_interrupts_enable(adapter_t *adapter)
+{
+ unsigned int i;
+ u32 pl_intr;
+
+ adapter->slow_intr_mask = F_PL_INTR_SGE_ERR;
+
+ t1_sge_intr_enable(adapter->sge);
+ if (adapter->espi) {
+ adapter->slow_intr_mask |= F_PL_INTR_ESPI;
+ t1_espi_intr_enable(adapter->espi);
+ }
+
+ /* Enable MAC/PHY interrupts for each port. */
+ for_each_port(adapter, i) {
+ adapter->port[i].mac->ops->interrupt_enable(adapter->port[i].mac);
+ adapter->port[i].phy->ops->interrupt_enable(adapter->port[i].phy);
+ }
+
+ /* Enable PCIX & external chip interrupts on ASIC boards. */
+ pl_intr = readl(adapter->regs + A_PL_ENABLE);
+
+ /* PCI-X interrupts */
+ pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_ENABLE,
+ 0xffffffff);
+
+ adapter->slow_intr_mask |= F_PL_INTR_EXT | F_PL_INTR_PCIX;
+ pl_intr |= F_PL_INTR_EXT | F_PL_INTR_PCIX;
+ writel(pl_intr, adapter->regs + A_PL_ENABLE);
+}
+
+/* Disables all interrupts. */
+void t1_interrupts_disable(adapter_t* adapter)
+{
+ unsigned int i;
+
+ t1_sge_intr_disable(adapter->sge);
+ if (adapter->espi)
+ t1_espi_intr_disable(adapter->espi);
+
+ /* Disable MAC/PHY interrupts for each port. */
+ for_each_port(adapter, i) {
+ adapter->port[i].mac->ops->interrupt_disable(adapter->port[i].mac);
+ adapter->port[i].phy->ops->interrupt_disable(adapter->port[i].phy);
+ }
+
+ /* Disable PCIX & external chip interrupts. */
+ writel(0, adapter->regs + A_PL_ENABLE);
+
+ /* PCI-X interrupts */
+ pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_ENABLE, 0);
+
+ adapter->slow_intr_mask = 0;
+}
+
+/* Clears all interrupts */
+void t1_interrupts_clear(adapter_t* adapter)
+{
+ unsigned int i;
+ u32 pl_intr;
+
+
+ t1_sge_intr_clear(adapter->sge);
+ if (adapter->espi)
+ t1_espi_intr_clear(adapter->espi);
+
+ /* Clear MAC/PHY interrupts for each port. */
+ for_each_port(adapter, i) {
+ adapter->port[i].mac->ops->interrupt_clear(adapter->port[i].mac);
+ adapter->port[i].phy->ops->interrupt_clear(adapter->port[i].phy);
+ }
+
+ /* Enable interrupts for external devices. */
+ pl_intr = readl(adapter->regs + A_PL_CAUSE);
+
+ writel(pl_intr | F_PL_INTR_EXT | F_PL_INTR_PCIX,
+ adapter->regs + A_PL_CAUSE);
+
+ /* PCI-X interrupts */
+ pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE, 0xffffffff);
+}
+
+/*
+ * Slow path interrupt handler for ASICs.
+ */
+int t1_slow_intr_handler(adapter_t *adapter)
+{
+ u32 cause = readl(adapter->regs + A_PL_CAUSE);
+
+ cause &= adapter->slow_intr_mask;
+ if (!cause)
+ return 0;
+ if (cause & F_PL_INTR_SGE_ERR)
+ t1_sge_intr_error_handler(adapter->sge);
+ if (cause & F_PL_INTR_ESPI)
+ t1_espi_intr_handler(adapter->espi);
+ if (cause & F_PL_INTR_PCIX)
+ t1_pci_intr_handler(adapter);
+ if (cause & F_PL_INTR_EXT)
+ t1_elmer0_ext_intr(adapter);
+
+ /* Clear the interrupts just processed. */
+ writel(cause, adapter->regs + A_PL_CAUSE);
+ (void)readl(adapter->regs + A_PL_CAUSE); /* flush writes */
+ return 1;
+}
+
+/* Pause deadlock avoidance parameters */
+#define DROP_MSEC 16
+#define DROP_PKTS_CNT 1
+
+static void set_csum_offload(adapter_t *adapter, u32 csum_bit, int enable)
+{
+ u32 val = readl(adapter->regs + A_TP_GLOBAL_CONFIG);
+
+ if (enable)
+ val |= csum_bit;
+ else
+ val &= ~csum_bit;
+ writel(val, adapter->regs + A_TP_GLOBAL_CONFIG);
+}
+
+void t1_tp_set_ip_checksum_offload(adapter_t *adapter, int enable)
+{
+ set_csum_offload(adapter, F_IP_CSUM, enable);
+}
+
+void t1_tp_set_udp_checksum_offload(adapter_t *adapter, int enable)
+{
+ set_csum_offload(adapter, F_UDP_CSUM, enable);
+}
+
+void t1_tp_set_tcp_checksum_offload(adapter_t *adapter, int enable)
+{
+ set_csum_offload(adapter, F_TCP_CSUM, enable);
+}
+
+static void t1_tp_reset(adapter_t *adapter, unsigned int tp_clk)
+{
+ u32 val;
+
+ val = F_TP_IN_CSPI_CPL | F_TP_IN_CSPI_CHECK_IP_CSUM |
+ F_TP_IN_CSPI_CHECK_TCP_CSUM | F_TP_IN_ESPI_ETHERNET;
+ val |= F_TP_IN_ESPI_CHECK_IP_CSUM |
+ F_TP_IN_ESPI_CHECK_TCP_CSUM;
+ writel(val, adapter->regs + A_TP_IN_CONFIG);
+ writel(F_TP_OUT_CSPI_CPL |
+ F_TP_OUT_ESPI_ETHERNET |
+ F_TP_OUT_ESPI_GENERATE_IP_CSUM |
+ F_TP_OUT_ESPI_GENERATE_TCP_CSUM,
+ adapter->regs + A_TP_OUT_CONFIG);
+
+ val = readl(adapter->regs + A_TP_GLOBAL_CONFIG);
+ val &= ~(F_IP_CSUM | F_UDP_CSUM | F_TCP_CSUM);
+ writel(val, adapter->regs + A_TP_GLOBAL_CONFIG);
+
+ /*
+ * Enable pause frame deadlock prevention.
+ */
+ if (is_T2(adapter)) {
+ u32 drop_ticks = DROP_MSEC * (tp_clk / 1000);
+
+ writel(F_ENABLE_TX_DROP | F_ENABLE_TX_ERROR |
+ V_DROP_TICKS_CNT(drop_ticks) |
+ V_NUM_PKTS_DROPPED(DROP_PKTS_CNT),
+ adapter->regs + A_TP_TX_DROP_CONFIG);
+ }
+
+ writel(F_TP_RESET, adapter->regs + A_TP_RESET);
+}
+
+int __devinit t1_get_board_rev(adapter_t *adapter, const struct board_info *bi,
+ struct adapter_params *p)
+{
+ p->chip_version = bi->chip_term;
+ if (p->chip_version == CHBT_TERM_T1 ||
+ p->chip_version == CHBT_TERM_T2) {
+ u32 val = readl(adapter->regs + A_TP_PC_CONFIG);
+
+ val = G_TP_PC_REV(val);
+ if (val == 2)
+ p->chip_revision = TERM_T1B;
+ else if (val == 3)
+ p->chip_revision = TERM_T2;
+ else
+ return -1;
+ } else
+ return -1;
+ return 0;
+}
+
+/*
+ * Enable board components other than the Chelsio chip, such as external MAC
+ * and PHY.
+ */
+static int board_init(adapter_t *adapter, const struct board_info *bi)
+{
+ switch (bi->board) {
+ case CHBT_BOARD_N110:
+ case CHBT_BOARD_N210:
+ writel(V_TPIPAR(0xf), adapter->regs + A_TPI_PAR);
+ t1_tpi_write(adapter, A_ELMER0_GPO, 0x800);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Initialize and configure the Terminator HW modules. Note that external
+ * MAC and PHYs are initialized separately.
+ */
+int t1_init_hw_modules(adapter_t *adapter)
+{
+ int err = -EIO;
+ const struct board_info *bi = board_info(adapter);
+
+ if (!bi->clock_mc4) {
+ u32 val = readl(adapter->regs + A_MC4_CFG);
+
+ writel(val | F_READY | F_MC4_SLOW, adapter->regs + A_MC4_CFG);
+ writel(F_M_BUS_ENABLE | F_TCAM_RESET,
+ adapter->regs + A_MC5_CONFIG);
+ }
+
+ if (adapter->espi && t1_espi_init(adapter->espi, bi->chip_mac,
+ bi->espi_nports))
+ goto out_err;
+
+ t1_tp_reset(adapter, bi->clock_core);
+
+ err = t1_sge_configure(adapter->sge, &adapter->params.sge);
+ if (err)
+ goto out_err;
+
+ err = 0;
+ out_err:
+ return err;
+}
+
+/*
+ * Determine a card's PCI mode.
+ */
+static void __devinit get_pci_mode(adapter_t *adapter, struct chelsio_pci_params *p)
+{
+ static unsigned short speed_map[] = { 33, 66, 100, 133 };
+ u32 pci_mode;
+
+ pci_read_config_dword(adapter->pdev, A_PCICFG_MODE, &pci_mode);
+ p->speed = speed_map[G_PCI_MODE_CLK(pci_mode)];
+ p->width = (pci_mode & F_PCI_MODE_64BIT) ? 64 : 32;
+ p->is_pcix = (pci_mode & F_PCI_MODE_PCIX) != 0;
+}
+
+/*
+ * Release the structures holding the SW per-Terminator-HW-module state.
+ */
+void t1_free_sw_modules(adapter_t *adapter)
+{
+ unsigned int i;
+
+ for_each_port(adapter, i) {
+ struct cmac *mac = adapter->port[i].mac;
+ struct cphy *phy = adapter->port[i].phy;
+
+ if (mac)
+ mac->ops->destroy(mac);
+ if (phy)
+ phy->ops->destroy(phy);
+ }
+
+ if (adapter->sge)
+ t1_sge_destroy(adapter->sge);
+ if (adapter->espi)
+ t1_espi_destroy(adapter->espi);
+}
+
+static void __devinit init_link_config(struct link_config *lc,
+ const struct board_info *bi)
+{
+ lc->supported = bi->caps;
+ lc->requested_speed = lc->speed = SPEED_INVALID;
+ lc->requested_duplex = lc->duplex = DUPLEX_INVALID;
+ lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX;
+ if (lc->supported & SUPPORTED_Autoneg) {
+ lc->advertising = lc->supported;
+ lc->autoneg = AUTONEG_ENABLE;
+ lc->requested_fc |= PAUSE_AUTONEG;
+ } else {
+ lc->advertising = 0;
+ lc->autoneg = AUTONEG_DISABLE;
+ }
+}
+
+
+/*
+ * Allocate and initialize the data structures that hold the SW state of
+ * the Terminator HW modules.
+ */
+int __devinit t1_init_sw_modules(adapter_t *adapter,
+ const struct board_info *bi)
+{
+ unsigned int i;
+
+ adapter->params.brd_info = bi;
+ adapter->params.nports = bi->port_number;
+ adapter->params.stats_update_period = bi->gmac->stats_update_period;
+
+ adapter->sge = t1_sge_create(adapter, &adapter->params.sge);
+ if (!adapter->sge) {
+ CH_ERR("%s: SGE initialization failed\n",
+ adapter->name);
+ goto error;
+ }
+
+ if (bi->espi_nports && !(adapter->espi = t1_espi_create(adapter))) {
+ CH_ERR("%s: ESPI initialization failed\n",
+ adapter->name);
+ goto error;
+ }
+
+ board_init(adapter, bi);
+ bi->mdio_ops->init(adapter, bi);
+ if (bi->gphy->reset)
+ bi->gphy->reset(adapter);
+ if (bi->gmac->reset)
+ bi->gmac->reset(adapter);
+
+ for_each_port(adapter, i) {
+ u8 hw_addr[6];
+ struct cmac *mac;
+ int phy_addr = bi->mdio_phybaseaddr + i;
+
+ adapter->port[i].phy = bi->gphy->create(adapter, phy_addr,
+ bi->mdio_ops);
+ if (!adapter->port[i].phy) {
+ CH_ERR("%s: PHY %d initialization failed\n",
+ adapter->name, i);
+ goto error;
+ }
+
+ adapter->port[i].mac = mac = bi->gmac->create(adapter, i);
+ if (!mac) {
+ CH_ERR("%s: MAC %d initialization failed\n",
+ adapter->name, i);
+ goto error;
+ }
+
+ /*
+ * Get the port's MAC addresses either from the EEPROM if one
+ * exists or the one hardcoded in the MAC.
+ */
+ if (vpd_macaddress_get(adapter, i, hw_addr)) {
+ CH_ERR("%s: could not read MAC address from VPD ROM\n",
+ adapter->port[i].dev->name);
+ goto error;
+ }
+ memcpy(adapter->port[i].dev->dev_addr, hw_addr, ETH_ALEN);
+ init_link_config(&adapter->port[i].link_config, bi);
+ }
+
+ get_pci_mode(adapter, &adapter->params.pci);
+ t1_interrupts_clear(adapter);
+ return 0;
+
+ error:
+ t1_free_sw_modules(adapter);
+ return -1;
+}
diff --git a/drivers/net/chelsio/suni1x10gexp_regs.h b/drivers/net/chelsio/suni1x10gexp_regs.h
new file mode 100644
index 000000000000..81816c2b708a
--- /dev/null
+++ b/drivers/net/chelsio/suni1x10gexp_regs.h
@@ -0,0 +1,213 @@
+/*****************************************************************************
+ * *
+ * File: suni1x10gexp_regs.h *
+ * $Revision: 1.9 $ *
+ * $Date: 2005/06/22 00:17:04 $ *
+ * Description: *
+ * PMC/SIERRA (pm3393) MAC-PHY functionality. *
+ * part of the Chelsio 10Gb Ethernet Driver. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License, version 2, as *
+ * published by the Free Software Foundation. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * Authors: PMC/SIERRA *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+
+#ifndef _CXGB_SUNI1x10GEXP_REGS_H_
+#define _CXGB_SUNI1x10GEXP_REGS_H_
+
+/******************************************************************************/
+/** S/UNI-1x10GE-XP REGISTER ADDRESS MAP **/
+/******************************************************************************/
+/* Refer to the Register Bit Masks bellow for the naming of each register and */
+/* to the S/UNI-1x10GE-XP Data Sheet for the signification of each bit */
+/******************************************************************************/
+
+#define SUNI1x10GEXP_REG_DEVICE_STATUS 0x0004
+#define SUNI1x10GEXP_REG_MASTER_INTERRUPT_STATUS 0x000D
+#define SUNI1x10GEXP_REG_GLOBAL_INTERRUPT_ENABLE 0x000E
+#define SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_ENABLE 0x0102
+#define SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_STATUS 0x0104
+#define SUNI1x10GEXP_REG_RXXG_CONFIG_1 0x2040
+#define SUNI1x10GEXP_REG_RXXG_CONFIG_3 0x2042
+#define SUNI1x10GEXP_REG_RXXG_INTERRUPT 0x2043
+#define SUNI1x10GEXP_REG_RXXG_MAX_FRAME_LENGTH 0x2045
+#define SUNI1x10GEXP_REG_RXXG_SA_15_0 0x2046
+#define SUNI1x10GEXP_REG_RXXG_SA_31_16 0x2047
+#define SUNI1x10GEXP_REG_RXXG_SA_47_32 0x2048
+#define SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_LOW 0x204D
+#define SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_MID 0x204E
+#define SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_HIGH 0x204F
+#define SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_LOW 0x206A
+#define SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDLOW 0x206B
+#define SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDHIGH 0x206C
+#define SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_HIGH 0x206D
+#define SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_0 0x206E
+#define SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_2 0x2070
+#define SUNI1x10GEXP_REG_XRF_INTERRUPT_ENABLE 0x2088
+#define SUNI1x10GEXP_REG_XRF_INTERRUPT_STATUS 0x2089
+#define SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_ENABLE 0x208B
+#define SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_STATUS 0x208C
+#define SUNI1x10GEXP_REG_RXOAM_INTERRUPT_ENABLE 0x20C7
+#define SUNI1x10GEXP_REG_RXOAM_INTERRUPT_STATUS 0x20C8
+#define SUNI1x10GEXP_REG_MSTAT_CONTROL 0x2100
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_0 0x2101
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_1 0x2102
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_2 0x2103
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_3 0x2104
+#define SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_0 0x2105
+#define SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_1 0x2106
+#define SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_2 0x2107
+#define SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_3 0x2108
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_0_LOW 0x2110
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_1_LOW 0x2114
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_4_LOW 0x2120
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_5_LOW 0x2124
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_6_LOW 0x2128
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_8_LOW 0x2130
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_10_LOW 0x2138
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_11_LOW 0x213C
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_12_LOW 0x2140
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_13_LOW 0x2144
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_15_LOW 0x214C
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_16_LOW 0x2150
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_17_LOW 0x2154
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_18_LOW 0x2158
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_33_LOW 0x2194
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_35_LOW 0x219C
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_36_LOW 0x21A0
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_38_LOW 0x21A8
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_40_LOW 0x21B0
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_42_LOW 0x21B8
+#define SUNI1x10GEXP_REG_MSTAT_COUNTER_43_LOW 0x21BC
+#define SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_ENABLE 0x2209
+#define SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_INTERRUPT 0x220A
+#define SUNI1x10GEXP_REG_PL4ODP_INTERRUPT_MASK 0x2282
+#define SUNI1x10GEXP_REG_PL4ODP_INTERRUPT 0x2283
+#define SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_STATUS 0x2300
+#define SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_CHANGE 0x2301
+#define SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_MASK 0x2302
+#define SUNI1x10GEXP_REG_TXXG_CONFIG_1 0x3040
+#define SUNI1x10GEXP_REG_TXXG_CONFIG_3 0x3042
+#define SUNI1x10GEXP_REG_TXXG_INTERRUPT 0x3043
+#define SUNI1x10GEXP_REG_TXXG_MAX_FRAME_SIZE 0x3045
+#define SUNI1x10GEXP_REG_TXXG_SA_15_0 0x3047
+#define SUNI1x10GEXP_REG_TXXG_SA_31_16 0x3048
+#define SUNI1x10GEXP_REG_TXXG_SA_47_32 0x3049
+#define SUNI1x10GEXP_REG_XTEF_INTERRUPT_STATUS 0x3084
+#define SUNI1x10GEXP_REG_XTEF_INTERRUPT_ENABLE 0x3085
+#define SUNI1x10GEXP_REG_TXOAM_INTERRUPT_ENABLE 0x30C6
+#define SUNI1x10GEXP_REG_TXOAM_INTERRUPT_STATUS 0x30C7
+#define SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_ENABLE 0x320C
+#define SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_INDICATION 0x320D
+#define SUNI1x10GEXP_REG_PL4IDU_INTERRUPT_MASK 0x3282
+#define SUNI1x10GEXP_REG_PL4IDU_INTERRUPT 0x3283
+
+/******************************************************************************/
+/* -- End register offset definitions -- */
+/******************************************************************************/
+
+/******************************************************************************/
+/** SUNI-1x10GE-XP REGISTER BIT MASKS **/
+/******************************************************************************/
+
+/*----------------------------------------------------------------------------
+ * Register 0x0004: S/UNI-1x10GE-XP Device Status
+ * Bit 9 TOP_SXRA_EXPIRED
+ * Bit 8 TOP_MDIO_BUSY
+ * Bit 7 TOP_DTRB
+ * Bit 6 TOP_EXPIRED
+ * Bit 5 TOP_PAUSED
+ * Bit 4 TOP_PL4_ID_DOOL
+ * Bit 3 TOP_PL4_IS_DOOL
+ * Bit 2 TOP_PL4_ID_ROOL
+ * Bit 1 TOP_PL4_IS_ROOL
+ * Bit 0 TOP_PL4_OUT_ROOL
+ *----------------------------------------------------------------------------*/
+#define SUNI1x10GEXP_BITMSK_TOP_SXRA_EXPIRED 0x0200
+#define SUNI1x10GEXP_BITMSK_TOP_EXPIRED 0x0040
+#define SUNI1x10GEXP_BITMSK_TOP_PL4_ID_DOOL 0x0010
+#define SUNI1x10GEXP_BITMSK_TOP_PL4_IS_DOOL 0x0008
+#define SUNI1x10GEXP_BITMSK_TOP_PL4_ID_ROOL 0x0004
+#define SUNI1x10GEXP_BITMSK_TOP_PL4_IS_ROOL 0x0002
+#define SUNI1x10GEXP_BITMSK_TOP_PL4_OUT_ROOL 0x0001
+
+/*----------------------------------------------------------------------------
+ * Register 0x000E:PM3393 Global interrupt enable
+ * Bit 15 TOP_INTE
+ *----------------------------------------------------------------------------*/
+#define SUNI1x10GEXP_BITMSK_TOP_INTE 0x8000
+
+/*----------------------------------------------------------------------------
+ * Register 0x2040: RXXG Configuration 1
+ * Bit 15 RXXG_RXEN
+ * Bit 14 RXXG_ROCF
+ * Bit 13 RXXG_PAD_STRIP
+ * Bit 10 RXXG_PUREP
+ * Bit 9 RXXG_LONGP
+ * Bit 8 RXXG_PARF
+ * Bit 7 RXXG_FLCHK
+ * Bit 5 RXXG_PASS_CTRL
+ * Bit 3 RXXG_CRC_STRIP
+ * Bit 2-0 RXXG_MIFG
+ *----------------------------------------------------------------------------*/
+#define SUNI1x10GEXP_BITMSK_RXXG_RXEN 0x8000
+#define SUNI1x10GEXP_BITMSK_RXXG_PUREP 0x0400
+#define SUNI1x10GEXP_BITMSK_RXXG_FLCHK 0x0080
+#define SUNI1x10GEXP_BITMSK_RXXG_CRC_STRIP 0x0008
+
+/*----------------------------------------------------------------------------
+ * Register 0x2070: RXXG Address Filter Control 2
+ * Bit 1 RXXG_PMODE
+ * Bit 0 RXXG_MHASH_EN
+ *----------------------------------------------------------------------------*/
+#define SUNI1x10GEXP_BITMSK_RXXG_PMODE 0x0002
+#define SUNI1x10GEXP_BITMSK_RXXG_MHASH_EN 0x0001
+
+/*----------------------------------------------------------------------------
+ * Register 0x2100: MSTAT Control
+ * Bit 2 MSTAT_WRITE
+ * Bit 1 MSTAT_CLEAR
+ * Bit 0 MSTAT_SNAP
+ *----------------------------------------------------------------------------*/
+#define SUNI1x10GEXP_BITMSK_MSTAT_CLEAR 0x0002
+#define SUNI1x10GEXP_BITMSK_MSTAT_SNAP 0x0001
+
+/*----------------------------------------------------------------------------
+ * Register 0x3040: TXXG Configuration Register 1
+ * Bit 15 TXXG_TXEN0
+ * Bit 13 TXXG_HOSTPAUSE
+ * Bit 12-7 TXXG_IPGT
+ * Bit 5 TXXG_32BIT_ALIGN
+ * Bit 4 TXXG_CRCEN
+ * Bit 3 TXXG_FCTX
+ * Bit 2 TXXG_FCRX
+ * Bit 1 TXXG_PADEN
+ * Bit 0 TXXG_SPRE
+ *----------------------------------------------------------------------------*/
+#define SUNI1x10GEXP_BITMSK_TXXG_TXEN0 0x8000
+#define SUNI1x10GEXP_BITOFF_TXXG_IPGT 7
+#define SUNI1x10GEXP_BITMSK_TXXG_32BIT_ALIGN 0x0020
+#define SUNI1x10GEXP_BITMSK_TXXG_CRCEN 0x0010
+#define SUNI1x10GEXP_BITMSK_TXXG_FCTX 0x0008
+#define SUNI1x10GEXP_BITMSK_TXXG_FCRX 0x0004
+#define SUNI1x10GEXP_BITMSK_TXXG_PADEN 0x0002
+
+#endif /* _CXGB_SUNI1x10GEXP_REGS_H_ */
+
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index d0fa2448761d..25cc20e415da 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -1,7 +1,7 @@
/*******************************************************************************
- Copyright(c) 1999 - 2004 Intel Corporation. All rights reserved.
+ Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
@@ -156,7 +156,7 @@
#define DRV_NAME "e100"
#define DRV_EXT "-NAPI"
-#define DRV_VERSION "3.4.8-k2"DRV_EXT
+#define DRV_VERSION "3.4.14-k2"DRV_EXT
#define DRV_DESCRIPTION "Intel(R) PRO/100 Network Driver"
#define DRV_COPYRIGHT "Copyright(c) 1999-2005 Intel Corporation"
#define PFX DRV_NAME ": "
@@ -785,6 +785,7 @@ static int e100_eeprom_save(struct nic *nic, u16 start, u16 count)
}
#define E100_WAIT_SCB_TIMEOUT 20000 /* we might have to wait 100ms!!! */
+#define E100_WAIT_SCB_FAST 20 /* delay like the old code */
static inline int e100_exec_cmd(struct nic *nic, u8 cmd, dma_addr_t dma_addr)
{
unsigned long flags;
@@ -798,7 +799,7 @@ static inline int e100_exec_cmd(struct nic *nic, u8 cmd, dma_addr_t dma_addr)
if(likely(!readb(&nic->csr->scb.cmd_lo)))
break;
cpu_relax();
- if(unlikely(i > (E100_WAIT_SCB_TIMEOUT >> 1)))
+ if(unlikely(i > E100_WAIT_SCB_FAST))
udelay(5);
}
if(unlikely(i == E100_WAIT_SCB_TIMEOUT)) {
@@ -902,8 +903,8 @@ static void mdio_write(struct net_device *netdev, int addr, int reg, int data)
static void e100_get_defaults(struct nic *nic)
{
- struct param_range rfds = { .min = 16, .max = 256, .count = 64 };
- struct param_range cbs = { .min = 64, .max = 256, .count = 64 };
+ struct param_range rfds = { .min = 16, .max = 256, .count = 256 };
+ struct param_range cbs = { .min = 64, .max = 256, .count = 128 };
pci_read_config_byte(nic->pdev, PCI_REVISION_ID, &nic->rev_id);
/* MAC type is encoded as rev ID; exception: ICH is treated as 82559 */
@@ -1006,25 +1007,213 @@ static void e100_configure(struct nic *nic, struct cb *cb, struct sk_buff *skb)
c[16], c[17], c[18], c[19], c[20], c[21], c[22], c[23]);
}
+/********************************************************/
+/* Micro code for 8086:1229 Rev 8 */
+/********************************************************/
+
+/* Parameter values for the D101M B-step */
+#define D101M_CPUSAVER_TIMER_DWORD 78
+#define D101M_CPUSAVER_BUNDLE_DWORD 65
+#define D101M_CPUSAVER_MIN_SIZE_DWORD 126
+
+#define D101M_B_RCVBUNDLE_UCODE \
+{\
+0x00550215, 0xFFFF0437, 0xFFFFFFFF, 0x06A70789, 0xFFFFFFFF, 0x0558FFFF, \
+0x000C0001, 0x00101312, 0x000C0008, 0x00380216, \
+0x0010009C, 0x00204056, 0x002380CC, 0x00380056, \
+0x0010009C, 0x00244C0B, 0x00000800, 0x00124818, \
+0x00380438, 0x00000000, 0x00140000, 0x00380555, \
+0x00308000, 0x00100662, 0x00100561, 0x000E0408, \
+0x00134861, 0x000C0002, 0x00103093, 0x00308000, \
+0x00100624, 0x00100561, 0x000E0408, 0x00100861, \
+0x000C007E, 0x00222C21, 0x000C0002, 0x00103093, \
+0x00380C7A, 0x00080000, 0x00103090, 0x00380C7A, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x0010009C, 0x00244C2D, 0x00010004, 0x00041000, \
+0x003A0437, 0x00044010, 0x0038078A, 0x00000000, \
+0x00100099, 0x00206C7A, 0x0010009C, 0x00244C48, \
+0x00130824, 0x000C0001, 0x00101213, 0x00260C75, \
+0x00041000, 0x00010004, 0x00130826, 0x000C0006, \
+0x002206A8, 0x0013C926, 0x00101313, 0x003806A8, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00080600, 0x00101B10, 0x00050004, 0x00100826, \
+0x00101210, 0x00380C34, 0x00000000, 0x00000000, \
+0x0021155B, 0x00100099, 0x00206559, 0x0010009C, \
+0x00244559, 0x00130836, 0x000C0000, 0x00220C62, \
+0x000C0001, 0x00101B13, 0x00229C0E, 0x00210C0E, \
+0x00226C0E, 0x00216C0E, 0x0022FC0E, 0x00215C0E, \
+0x00214C0E, 0x00380555, 0x00010004, 0x00041000, \
+0x00278C67, 0x00040800, 0x00018100, 0x003A0437, \
+0x00130826, 0x000C0001, 0x00220559, 0x00101313, \
+0x00380559, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00130831, 0x0010090B, 0x00124813, \
+0x000CFF80, 0x002606AB, 0x00041000, 0x00010004, \
+0x003806A8, 0x00000000, 0x00000000, 0x00000000, \
+}
+
+/********************************************************/
+/* Micro code for 8086:1229 Rev 9 */
+/********************************************************/
+
+/* Parameter values for the D101S */
+#define D101S_CPUSAVER_TIMER_DWORD 78
+#define D101S_CPUSAVER_BUNDLE_DWORD 67
+#define D101S_CPUSAVER_MIN_SIZE_DWORD 128
+
+#define D101S_RCVBUNDLE_UCODE \
+{\
+0x00550242, 0xFFFF047E, 0xFFFFFFFF, 0x06FF0818, 0xFFFFFFFF, 0x05A6FFFF, \
+0x000C0001, 0x00101312, 0x000C0008, 0x00380243, \
+0x0010009C, 0x00204056, 0x002380D0, 0x00380056, \
+0x0010009C, 0x00244F8B, 0x00000800, 0x00124818, \
+0x0038047F, 0x00000000, 0x00140000, 0x003805A3, \
+0x00308000, 0x00100610, 0x00100561, 0x000E0408, \
+0x00134861, 0x000C0002, 0x00103093, 0x00308000, \
+0x00100624, 0x00100561, 0x000E0408, 0x00100861, \
+0x000C007E, 0x00222FA1, 0x000C0002, 0x00103093, \
+0x00380F90, 0x00080000, 0x00103090, 0x00380F90, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x0010009C, 0x00244FAD, 0x00010004, 0x00041000, \
+0x003A047E, 0x00044010, 0x00380819, 0x00000000, \
+0x00100099, 0x00206FFD, 0x0010009A, 0x0020AFFD, \
+0x0010009C, 0x00244FC8, 0x00130824, 0x000C0001, \
+0x00101213, 0x00260FF7, 0x00041000, 0x00010004, \
+0x00130826, 0x000C0006, 0x00220700, 0x0013C926, \
+0x00101313, 0x00380700, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00080600, 0x00101B10, 0x00050004, 0x00100826, \
+0x00101210, 0x00380FB6, 0x00000000, 0x00000000, \
+0x002115A9, 0x00100099, 0x002065A7, 0x0010009A, \
+0x0020A5A7, 0x0010009C, 0x002445A7, 0x00130836, \
+0x000C0000, 0x00220FE4, 0x000C0001, 0x00101B13, \
+0x00229F8E, 0x00210F8E, 0x00226F8E, 0x00216F8E, \
+0x0022FF8E, 0x00215F8E, 0x00214F8E, 0x003805A3, \
+0x00010004, 0x00041000, 0x00278FE9, 0x00040800, \
+0x00018100, 0x003A047E, 0x00130826, 0x000C0001, \
+0x002205A7, 0x00101313, 0x003805A7, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00130831, \
+0x0010090B, 0x00124813, 0x000CFF80, 0x00260703, \
+0x00041000, 0x00010004, 0x00380700 \
+}
+
+/********************************************************/
+/* Micro code for the 8086:1229 Rev F/10 */
+/********************************************************/
+
+/* Parameter values for the D102 E-step */
+#define D102_E_CPUSAVER_TIMER_DWORD 42
+#define D102_E_CPUSAVER_BUNDLE_DWORD 54
+#define D102_E_CPUSAVER_MIN_SIZE_DWORD 46
+
+#define D102_E_RCVBUNDLE_UCODE \
+{\
+0x007D028F, 0x0E4204F9, 0x14ED0C85, 0x14FA14E9, 0x0EF70E36, 0x1FFF1FFF, \
+0x00E014B9, 0x00000000, 0x00000000, 0x00000000, \
+0x00E014BD, 0x00000000, 0x00000000, 0x00000000, \
+0x00E014D5, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00E014C1, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00E014C8, 0x00000000, 0x00000000, 0x00000000, \
+0x00200600, 0x00E014EE, 0x00000000, 0x00000000, \
+0x0030FF80, 0x00940E46, 0x00038200, 0x00102000, \
+0x00E00E43, 0x00000000, 0x00000000, 0x00000000, \
+0x00300006, 0x00E014FB, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00906E41, 0x00800E3C, 0x00E00E39, 0x00000000, \
+0x00906EFD, 0x00900EFD, 0x00E00EF8, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+}
+
static void e100_load_ucode(struct nic *nic, struct cb *cb, struct sk_buff *skb)
{
- int i;
- static const u32 ucode[UCODE_SIZE] = {
- /* NFS packets are misinterpreted as TCO packets and
- * incorrectly routed to the BMC over SMBus. This
- * microcode patch checks the fragmented IP bit in the
- * NFS/UDP header to distinguish between NFS and TCO. */
- 0x0EF70E36, 0x1FFF1FFF, 0x1FFF1FFF, 0x1FFF1FFF, 0x1FFF1FFF,
- 0x1FFF1FFF, 0x00906E41, 0x00800E3C, 0x00E00E39, 0x00000000,
- 0x00906EFD, 0x00900EFD, 0x00E00EF8,
- };
+/* *INDENT-OFF* */
+ static struct {
+ u32 ucode[UCODE_SIZE + 1];
+ u8 mac;
+ u8 timer_dword;
+ u8 bundle_dword;
+ u8 min_size_dword;
+ } ucode_opts[] = {
+ { D101M_B_RCVBUNDLE_UCODE,
+ mac_82559_D101M,
+ D101M_CPUSAVER_TIMER_DWORD,
+ D101M_CPUSAVER_BUNDLE_DWORD,
+ D101M_CPUSAVER_MIN_SIZE_DWORD },
+ { D101S_RCVBUNDLE_UCODE,
+ mac_82559_D101S,
+ D101S_CPUSAVER_TIMER_DWORD,
+ D101S_CPUSAVER_BUNDLE_DWORD,
+ D101S_CPUSAVER_MIN_SIZE_DWORD },
+ { D102_E_RCVBUNDLE_UCODE,
+ mac_82551_F,
+ D102_E_CPUSAVER_TIMER_DWORD,
+ D102_E_CPUSAVER_BUNDLE_DWORD,
+ D102_E_CPUSAVER_MIN_SIZE_DWORD },
+ { D102_E_RCVBUNDLE_UCODE,
+ mac_82551_10,
+ D102_E_CPUSAVER_TIMER_DWORD,
+ D102_E_CPUSAVER_BUNDLE_DWORD,
+ D102_E_CPUSAVER_MIN_SIZE_DWORD },
+ { {0}, 0, 0, 0, 0}
+ }, *opts;
+/* *INDENT-ON* */
+
+#define BUNDLESMALL 1
+#define BUNDLEMAX 50
+#define INTDELAY 15000
+
+ opts = ucode_opts;
+
+ /* do not load u-code for ICH devices */
+ if (nic->flags & ich)
+ return;
+
+ /* Search for ucode match against h/w rev_id */
+ while (opts->mac) {
+ if (nic->mac == opts->mac) {
+ int i;
+ u32 *ucode = opts->ucode;
+
+ /* Insert user-tunable settings */
+ ucode[opts->timer_dword] &= 0xFFFF0000;
+ ucode[opts->timer_dword] |=
+ (u16) INTDELAY;
+ ucode[opts->bundle_dword] &= 0xFFFF0000;
+ ucode[opts->bundle_dword] |= (u16) BUNDLEMAX;
+ ucode[opts->min_size_dword] &= 0xFFFF0000;
+ ucode[opts->min_size_dword] |=
+ (BUNDLESMALL) ? 0xFFFF : 0xFF80;
+
+ for(i = 0; i < UCODE_SIZE; i++)
+ cb->u.ucode[i] = cpu_to_le32(ucode[i]);
+ cb->command = cpu_to_le16(cb_ucode);
+ return;
+ }
+ opts++;
+ }
- if(nic->mac == mac_82551_F || nic->mac == mac_82551_10) {
- for(i = 0; i < UCODE_SIZE; i++)
- cb->u.ucode[i] = cpu_to_le32(ucode[i]);
- cb->command = cpu_to_le16(cb_ucode);
- } else
- cb->command = cpu_to_le16(cb_nop);
+ cb->command = cpu_to_le16(cb_nop);
}
static void e100_setup_iaaddr(struct nic *nic, struct cb *cb,
@@ -1307,14 +1496,15 @@ static inline void e100_xmit_prepare(struct nic *nic, struct cb *cb,
{
cb->command = nic->tx_command;
/* interrupt every 16 packets regardless of delay */
- if((nic->cbs_avail & ~15) == nic->cbs_avail) cb->command |= cb_i;
+ if((nic->cbs_avail & ~15) == nic->cbs_avail)
+ cb->command |= cpu_to_le16(cb_i);
cb->u.tcb.tbd_array = cb->dma_addr + offsetof(struct cb, u.tcb.tbd);
cb->u.tcb.tcb_byte_count = 0;
cb->u.tcb.threshold = nic->tx_threshold;
cb->u.tcb.tbd_count = 1;
cb->u.tcb.tbd.buf_addr = cpu_to_le32(pci_map_single(nic->pdev,
skb->data, skb->len, PCI_DMA_TODEVICE));
- // check for mapping failure?
+ /* check for mapping failure? */
cb->u.tcb.tbd.size = cpu_to_le16(skb->len);
}
@@ -1539,7 +1729,7 @@ static inline int e100_rx_indicate(struct nic *nic, struct rx *rx,
/* Don't indicate if hardware indicates errors */
nic->net_stats.rx_dropped++;
dev_kfree_skb_any(skb);
- } else if(actual_size > nic->netdev->mtu + VLAN_ETH_HLEN) {
+ } else if(actual_size > ETH_DATA_LEN + VLAN_ETH_HLEN) {
/* Don't indicate oversized frames */
nic->rx_over_length_errors++;
nic->net_stats.rx_dropped++;
@@ -1706,6 +1896,7 @@ static int e100_poll(struct net_device *netdev, int *budget)
static void e100_netpoll(struct net_device *netdev)
{
struct nic *nic = netdev_priv(netdev);
+
e100_disable_irq(nic);
e100_intr(nic->pdev->irq, netdev, NULL);
e100_tx_clean(nic);
@@ -2108,6 +2299,8 @@ static void e100_diag_test(struct net_device *netdev,
}
for(i = 0; i < E100_TEST_LEN; i++)
test->flags |= data[i] ? ETH_TEST_FL_FAILED : 0;
+
+ msleep_interruptible(4 * 1000);
}
static int e100_phys_id(struct net_device *netdev, u32 data)
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index ba9f0580e1f9..2946e037a9b1 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -98,7 +98,7 @@ static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
static char bpq_eth_addr[6];
-static int bpq_rcv(struct sk_buff *, struct net_device *, struct packet_type *);
+static int bpq_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *);
static int bpq_device_event(struct notifier_block *, unsigned long, void *);
static const char *bpq_print_ethaddr(const unsigned char *);
@@ -165,7 +165,7 @@ static inline int dev_is_ethdev(struct net_device *dev)
/*
* Receive an AX.25 frame via an ethernet interface.
*/
-static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype)
+static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev)
{
int len;
char * ptr;
diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c
index c39b0609742a..32d5fabd4b10 100644
--- a/drivers/net/ibmveth.c
+++ b/drivers/net/ibmveth.c
@@ -1144,7 +1144,7 @@ static void ibmveth_proc_unregister_driver(void)
static struct vio_device_id ibmveth_device_table[] __devinitdata= {
{ "network", "IBM,l-lan"},
- { 0,}
+ { "", "" }
};
MODULE_DEVICE_TABLE(vio, ibmveth_device_table);
diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c
index 55af32e9bf08..dc5d089bf184 100644
--- a/drivers/net/iseries_veth.c
+++ b/drivers/net/iseries_veth.c
@@ -79,12 +79,55 @@
#include <asm/iommu.h>
#include <asm/vio.h>
-#include "iseries_veth.h"
+#undef DEBUG
MODULE_AUTHOR("Kyle Lucke <klucke@us.ibm.com>");
MODULE_DESCRIPTION("iSeries Virtual ethernet driver");
MODULE_LICENSE("GPL");
+#define VETH_EVENT_CAP (0)
+#define VETH_EVENT_FRAMES (1)
+#define VETH_EVENT_MONITOR (2)
+#define VETH_EVENT_FRAMES_ACK (3)
+
+#define VETH_MAX_ACKS_PER_MSG (20)
+#define VETH_MAX_FRAMES_PER_MSG (6)
+
+struct veth_frames_data {
+ u32 addr[VETH_MAX_FRAMES_PER_MSG];
+ u16 len[VETH_MAX_FRAMES_PER_MSG];
+ u32 eofmask;
+};
+#define VETH_EOF_SHIFT (32-VETH_MAX_FRAMES_PER_MSG)
+
+struct veth_frames_ack_data {
+ u16 token[VETH_MAX_ACKS_PER_MSG];
+};
+
+struct veth_cap_data {
+ u8 caps_version;
+ u8 rsvd1;
+ u16 num_buffers;
+ u16 ack_threshold;
+ u16 rsvd2;
+ u32 ack_timeout;
+ u32 rsvd3;
+ u64 rsvd4[3];
+};
+
+struct veth_lpevent {
+ struct HvLpEvent base_event;
+ union {
+ struct veth_cap_data caps_data;
+ struct veth_frames_data frames_data;
+ struct veth_frames_ack_data frames_ack_data;
+ } u;
+
+};
+
+#define DRV_NAME "iseries_veth"
+#define DRV_VERSION "2.0"
+
#define VETH_NUMBUFFERS (120)
#define VETH_ACKTIMEOUT (1000000) /* microseconds */
#define VETH_MAX_MCAST (12)
@@ -113,9 +156,9 @@ MODULE_LICENSE("GPL");
struct veth_msg {
struct veth_msg *next;
- struct VethFramesData data;
+ struct veth_frames_data data;
int token;
- unsigned long in_use;
+ int in_use;
struct sk_buff *skb;
struct device *dev;
};
@@ -125,23 +168,28 @@ struct veth_lpar_connection {
struct work_struct statemachine_wq;
struct veth_msg *msgs;
int num_events;
- struct VethCapData local_caps;
+ struct veth_cap_data local_caps;
+ struct kobject kobject;
struct timer_list ack_timer;
+ struct timer_list reset_timer;
+ unsigned int reset_timeout;
+ unsigned long last_contact;
+ int outstanding_tx;
+
spinlock_t lock;
unsigned long state;
HvLpInstanceId src_inst;
HvLpInstanceId dst_inst;
- struct VethLpEvent cap_event, cap_ack_event;
+ struct veth_lpevent cap_event, cap_ack_event;
u16 pending_acks[VETH_MAX_ACKS_PER_MSG];
u32 num_pending_acks;
int num_ack_events;
- struct VethCapData remote_caps;
+ struct veth_cap_data remote_caps;
u32 ack_timeout;
- spinlock_t msg_stack_lock;
struct veth_msg *msg_stack_head;
};
@@ -151,15 +199,17 @@ struct veth_port {
u64 mac_addr;
HvLpIndexMap lpar_map;
- spinlock_t pending_gate;
- struct sk_buff *pending_skb;
- HvLpIndexMap pending_lpmask;
+ /* queue_lock protects the stopped_map and dev's queue. */
+ spinlock_t queue_lock;
+ HvLpIndexMap stopped_map;
+ /* mcast_gate protects promiscuous, num_mcast & mcast_addr. */
rwlock_t mcast_gate;
int promiscuous;
- int all_mcast;
int num_mcast;
u64 mcast_addr[VETH_MAX_MCAST];
+
+ struct kobject kobject;
};
static HvLpIndex this_lp;
@@ -168,44 +218,56 @@ static struct net_device *veth_dev[HVMAXARCHITECTEDVIRTUALLANS]; /* = 0 */
static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev);
static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *);
-static void veth_flush_pending(struct veth_lpar_connection *cnx);
-static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *);
-static void veth_timed_ack(unsigned long connectionPtr);
+static void veth_wake_queues(struct veth_lpar_connection *cnx);
+static void veth_stop_queues(struct veth_lpar_connection *cnx);
+static void veth_receive(struct veth_lpar_connection *, struct veth_lpevent *);
+static void veth_release_connection(struct kobject *kobject);
+static void veth_timed_ack(unsigned long ptr);
+static void veth_timed_reset(unsigned long ptr);
/*
* Utility functions
*/
-#define veth_printk(prio, fmt, args...) \
- printk(prio "%s: " fmt, __FILE__, ## args)
+#define veth_info(fmt, args...) \
+ printk(KERN_INFO DRV_NAME ": " fmt, ## args)
#define veth_error(fmt, args...) \
- printk(KERN_ERR "(%s:%3.3d) ERROR: " fmt, __FILE__, __LINE__ , ## args)
+ printk(KERN_ERR DRV_NAME ": Error: " fmt, ## args)
+
+#ifdef DEBUG
+#define veth_debug(fmt, args...) \
+ printk(KERN_DEBUG DRV_NAME ": " fmt, ## args)
+#else
+#define veth_debug(fmt, args...) do {} while (0)
+#endif
+/* You must hold the connection's lock when you call this function. */
static inline void veth_stack_push(struct veth_lpar_connection *cnx,
struct veth_msg *msg)
{
- unsigned long flags;
-
- spin_lock_irqsave(&cnx->msg_stack_lock, flags);
msg->next = cnx->msg_stack_head;
cnx->msg_stack_head = msg;
- spin_unlock_irqrestore(&cnx->msg_stack_lock, flags);
}
+/* You must hold the connection's lock when you call this function. */
static inline struct veth_msg *veth_stack_pop(struct veth_lpar_connection *cnx)
{
- unsigned long flags;
struct veth_msg *msg;
- spin_lock_irqsave(&cnx->msg_stack_lock, flags);
msg = cnx->msg_stack_head;
if (msg)
cnx->msg_stack_head = cnx->msg_stack_head->next;
- spin_unlock_irqrestore(&cnx->msg_stack_lock, flags);
+
return msg;
}
+/* You must hold the connection's lock when you call this function. */
+static inline int veth_stack_is_empty(struct veth_lpar_connection *cnx)
+{
+ return cnx->msg_stack_head == NULL;
+}
+
static inline HvLpEvent_Rc
veth_signalevent(struct veth_lpar_connection *cnx, u16 subtype,
HvLpEvent_AckInd ackind, HvLpEvent_AckType acktype,
@@ -249,7 +311,7 @@ static int veth_allocate_events(HvLpIndex rlp, int number)
struct veth_allocation vc = { COMPLETION_INITIALIZER(vc.c), 0 };
mf_allocate_lp_events(rlp, HvLpEvent_Type_VirtualLan,
- sizeof(struct VethLpEvent), number,
+ sizeof(struct veth_lpevent), number,
&veth_complete_allocation, &vc);
wait_for_completion(&vc.c);
@@ -257,6 +319,137 @@ static int veth_allocate_events(HvLpIndex rlp, int number)
}
/*
+ * sysfs support
+ */
+
+struct veth_cnx_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct veth_lpar_connection *, char *buf);
+ ssize_t (*store)(struct veth_lpar_connection *, const char *buf);
+};
+
+static ssize_t veth_cnx_attribute_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct veth_cnx_attribute *cnx_attr;
+ struct veth_lpar_connection *cnx;
+
+ cnx_attr = container_of(attr, struct veth_cnx_attribute, attr);
+ cnx = container_of(kobj, struct veth_lpar_connection, kobject);
+
+ if (!cnx_attr->show)
+ return -EIO;
+
+ return cnx_attr->show(cnx, buf);
+}
+
+#define CUSTOM_CNX_ATTR(_name, _format, _expression) \
+static ssize_t _name##_show(struct veth_lpar_connection *cnx, char *buf)\
+{ \
+ return sprintf(buf, _format, _expression); \
+} \
+struct veth_cnx_attribute veth_cnx_attr_##_name = __ATTR_RO(_name)
+
+#define SIMPLE_CNX_ATTR(_name) \
+ CUSTOM_CNX_ATTR(_name, "%lu\n", (unsigned long)cnx->_name)
+
+SIMPLE_CNX_ATTR(outstanding_tx);
+SIMPLE_CNX_ATTR(remote_lp);
+SIMPLE_CNX_ATTR(num_events);
+SIMPLE_CNX_ATTR(src_inst);
+SIMPLE_CNX_ATTR(dst_inst);
+SIMPLE_CNX_ATTR(num_pending_acks);
+SIMPLE_CNX_ATTR(num_ack_events);
+CUSTOM_CNX_ATTR(ack_timeout, "%d\n", jiffies_to_msecs(cnx->ack_timeout));
+CUSTOM_CNX_ATTR(reset_timeout, "%d\n", jiffies_to_msecs(cnx->reset_timeout));
+CUSTOM_CNX_ATTR(state, "0x%.4lX\n", cnx->state);
+CUSTOM_CNX_ATTR(last_contact, "%d\n", cnx->last_contact ?
+ jiffies_to_msecs(jiffies - cnx->last_contact) : 0);
+
+#define GET_CNX_ATTR(_name) (&veth_cnx_attr_##_name.attr)
+
+static struct attribute *veth_cnx_default_attrs[] = {
+ GET_CNX_ATTR(outstanding_tx),
+ GET_CNX_ATTR(remote_lp),
+ GET_CNX_ATTR(num_events),
+ GET_CNX_ATTR(reset_timeout),
+ GET_CNX_ATTR(last_contact),
+ GET_CNX_ATTR(state),
+ GET_CNX_ATTR(src_inst),
+ GET_CNX_ATTR(dst_inst),
+ GET_CNX_ATTR(num_pending_acks),
+ GET_CNX_ATTR(num_ack_events),
+ GET_CNX_ATTR(ack_timeout),
+ NULL
+};
+
+static struct sysfs_ops veth_cnx_sysfs_ops = {
+ .show = veth_cnx_attribute_show
+};
+
+static struct kobj_type veth_lpar_connection_ktype = {
+ .release = veth_release_connection,
+ .sysfs_ops = &veth_cnx_sysfs_ops,
+ .default_attrs = veth_cnx_default_attrs
+};
+
+struct veth_port_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct veth_port *, char *buf);
+ ssize_t (*store)(struct veth_port *, const char *buf);
+};
+
+static ssize_t veth_port_attribute_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct veth_port_attribute *port_attr;
+ struct veth_port *port;
+
+ port_attr = container_of(attr, struct veth_port_attribute, attr);
+ port = container_of(kobj, struct veth_port, kobject);
+
+ if (!port_attr->show)
+ return -EIO;
+
+ return port_attr->show(port, buf);
+}
+
+#define CUSTOM_PORT_ATTR(_name, _format, _expression) \
+static ssize_t _name##_show(struct veth_port *port, char *buf) \
+{ \
+ return sprintf(buf, _format, _expression); \
+} \
+struct veth_port_attribute veth_port_attr_##_name = __ATTR_RO(_name)
+
+#define SIMPLE_PORT_ATTR(_name) \
+ CUSTOM_PORT_ATTR(_name, "%lu\n", (unsigned long)port->_name)
+
+SIMPLE_PORT_ATTR(promiscuous);
+SIMPLE_PORT_ATTR(num_mcast);
+CUSTOM_PORT_ATTR(lpar_map, "0x%X\n", port->lpar_map);
+CUSTOM_PORT_ATTR(stopped_map, "0x%X\n", port->stopped_map);
+CUSTOM_PORT_ATTR(mac_addr, "0x%lX\n", port->mac_addr);
+
+#define GET_PORT_ATTR(_name) (&veth_port_attr_##_name.attr)
+static struct attribute *veth_port_default_attrs[] = {
+ GET_PORT_ATTR(mac_addr),
+ GET_PORT_ATTR(lpar_map),
+ GET_PORT_ATTR(stopped_map),
+ GET_PORT_ATTR(promiscuous),
+ GET_PORT_ATTR(num_mcast),
+ NULL
+};
+
+static struct sysfs_ops veth_port_sysfs_ops = {
+ .show = veth_port_attribute_show
+};
+
+static struct kobj_type veth_port_ktype = {
+ .sysfs_ops = &veth_port_sysfs_ops,
+ .default_attrs = veth_port_default_attrs
+};
+
+/*
* LPAR connection code
*/
@@ -266,7 +459,7 @@ static inline void veth_kick_statemachine(struct veth_lpar_connection *cnx)
}
static void veth_take_cap(struct veth_lpar_connection *cnx,
- struct VethLpEvent *event)
+ struct veth_lpevent *event)
{
unsigned long flags;
@@ -278,7 +471,7 @@ static void veth_take_cap(struct veth_lpar_connection *cnx,
HvLpEvent_Type_VirtualLan);
if (cnx->state & VETH_STATE_GOTCAPS) {
- veth_error("Received a second capabilities from lpar %d\n",
+ veth_error("Received a second capabilities from LPAR %d.\n",
cnx->remote_lp);
event->base_event.xRc = HvLpEvent_Rc_BufferNotAvailable;
HvCallEvent_ackLpEvent((struct HvLpEvent *) event);
@@ -291,13 +484,13 @@ static void veth_take_cap(struct veth_lpar_connection *cnx,
}
static void veth_take_cap_ack(struct veth_lpar_connection *cnx,
- struct VethLpEvent *event)
+ struct veth_lpevent *event)
{
unsigned long flags;
spin_lock_irqsave(&cnx->lock, flags);
if (cnx->state & VETH_STATE_GOTCAPACK) {
- veth_error("Received a second capabilities ack from lpar %d\n",
+ veth_error("Received a second capabilities ack from LPAR %d.\n",
cnx->remote_lp);
} else {
memcpy(&cnx->cap_ack_event, event,
@@ -309,19 +502,24 @@ static void veth_take_cap_ack(struct veth_lpar_connection *cnx,
}
static void veth_take_monitor_ack(struct veth_lpar_connection *cnx,
- struct VethLpEvent *event)
+ struct veth_lpevent *event)
{
unsigned long flags;
spin_lock_irqsave(&cnx->lock, flags);
- veth_printk(KERN_DEBUG, "Monitor ack returned for lpar %d\n",
- cnx->remote_lp);
- cnx->state |= VETH_STATE_RESET;
- veth_kick_statemachine(cnx);
+ veth_debug("cnx %d: lost connection.\n", cnx->remote_lp);
+
+ /* Avoid kicking the statemachine once we're shutdown.
+ * It's unnecessary and it could break veth_stop_connection(). */
+
+ if (! (cnx->state & VETH_STATE_SHUTDOWN)) {
+ cnx->state |= VETH_STATE_RESET;
+ veth_kick_statemachine(cnx);
+ }
spin_unlock_irqrestore(&cnx->lock, flags);
}
-static void veth_handle_ack(struct VethLpEvent *event)
+static void veth_handle_ack(struct veth_lpevent *event)
{
HvLpIndex rlp = event->base_event.xTargetLp;
struct veth_lpar_connection *cnx = veth_cnx[rlp];
@@ -329,58 +527,67 @@ static void veth_handle_ack(struct VethLpEvent *event)
BUG_ON(! cnx);
switch (event->base_event.xSubtype) {
- case VethEventTypeCap:
+ case VETH_EVENT_CAP:
veth_take_cap_ack(cnx, event);
break;
- case VethEventTypeMonitor:
+ case VETH_EVENT_MONITOR:
veth_take_monitor_ack(cnx, event);
break;
default:
- veth_error("Unknown ack type %d from lpar %d\n",
- event->base_event.xSubtype, rlp);
+ veth_error("Unknown ack type %d from LPAR %d.\n",
+ event->base_event.xSubtype, rlp);
};
}
-static void veth_handle_int(struct VethLpEvent *event)
+static void veth_handle_int(struct veth_lpevent *event)
{
HvLpIndex rlp = event->base_event.xSourceLp;
struct veth_lpar_connection *cnx = veth_cnx[rlp];
unsigned long flags;
- int i;
+ int i, acked = 0;
BUG_ON(! cnx);
switch (event->base_event.xSubtype) {
- case VethEventTypeCap:
+ case VETH_EVENT_CAP:
veth_take_cap(cnx, event);
break;
- case VethEventTypeMonitor:
+ case VETH_EVENT_MONITOR:
/* do nothing... this'll hang out here til we're dead,
* and the hypervisor will return it for us. */
break;
- case VethEventTypeFramesAck:
+ case VETH_EVENT_FRAMES_ACK:
spin_lock_irqsave(&cnx->lock, flags);
+
for (i = 0; i < VETH_MAX_ACKS_PER_MSG; ++i) {
u16 msgnum = event->u.frames_ack_data.token[i];
- if (msgnum < VETH_NUMBUFFERS)
+ if (msgnum < VETH_NUMBUFFERS) {
veth_recycle_msg(cnx, cnx->msgs + msgnum);
+ cnx->outstanding_tx--;
+ acked++;
+ }
+ }
+
+ if (acked > 0) {
+ cnx->last_contact = jiffies;
+ veth_wake_queues(cnx);
}
+
spin_unlock_irqrestore(&cnx->lock, flags);
- veth_flush_pending(cnx);
break;
- case VethEventTypeFrames:
+ case VETH_EVENT_FRAMES:
veth_receive(cnx, event);
break;
default:
- veth_error("Unknown interrupt type %d from lpar %d\n",
- event->base_event.xSubtype, rlp);
+ veth_error("Unknown interrupt type %d from LPAR %d.\n",
+ event->base_event.xSubtype, rlp);
};
}
static void veth_handle_event(struct HvLpEvent *event, struct pt_regs *regs)
{
- struct VethLpEvent *veth_event = (struct VethLpEvent *)event;
+ struct veth_lpevent *veth_event = (struct veth_lpevent *)event;
if (event->xFlags.xFunction == HvLpEvent_Function_Ack)
veth_handle_ack(veth_event);
@@ -390,7 +597,7 @@ static void veth_handle_event(struct HvLpEvent *event, struct pt_regs *regs)
static int veth_process_caps(struct veth_lpar_connection *cnx)
{
- struct VethCapData *remote_caps = &cnx->remote_caps;
+ struct veth_cap_data *remote_caps = &cnx->remote_caps;
int num_acks_needed;
/* Convert timer to jiffies */
@@ -400,8 +607,8 @@ static int veth_process_caps(struct veth_lpar_connection *cnx)
|| (remote_caps->ack_threshold > VETH_MAX_ACKS_PER_MSG)
|| (remote_caps->ack_threshold == 0)
|| (cnx->ack_timeout == 0) ) {
- veth_error("Received incompatible capabilities from lpar %d\n",
- cnx->remote_lp);
+ veth_error("Received incompatible capabilities from LPAR %d.\n",
+ cnx->remote_lp);
return HvLpEvent_Rc_InvalidSubtypeData;
}
@@ -418,8 +625,8 @@ static int veth_process_caps(struct veth_lpar_connection *cnx)
cnx->num_ack_events += num;
if (cnx->num_ack_events < num_acks_needed) {
- veth_error("Couldn't allocate enough ack events for lpar %d\n",
- cnx->remote_lp);
+ veth_error("Couldn't allocate enough ack events "
+ "for LPAR %d.\n", cnx->remote_lp);
return HvLpEvent_Rc_BufferNotAvailable;
}
@@ -440,15 +647,15 @@ static void veth_statemachine(void *p)
restart:
if (cnx->state & VETH_STATE_RESET) {
- int i;
-
- del_timer(&cnx->ack_timer);
-
if (cnx->state & VETH_STATE_OPEN)
HvCallEvent_closeLpEventPath(cnx->remote_lp,
HvLpEvent_Type_VirtualLan);
- /* reset ack data */
+ /*
+ * Reset ack data. This prevents the ack_timer actually
+ * doing anything, even if it runs one more time when
+ * we drop the lock below.
+ */
memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks));
cnx->num_pending_acks = 0;
@@ -458,14 +665,32 @@ static void veth_statemachine(void *p)
| VETH_STATE_SENTCAPACK | VETH_STATE_READY);
/* Clean up any leftover messages */
- if (cnx->msgs)
+ if (cnx->msgs) {
+ int i;
for (i = 0; i < VETH_NUMBUFFERS; ++i)
veth_recycle_msg(cnx, cnx->msgs + i);
+ }
+
+ cnx->outstanding_tx = 0;
+ veth_wake_queues(cnx);
+
+ /* Drop the lock so we can do stuff that might sleep or
+ * take other locks. */
spin_unlock_irq(&cnx->lock);
- veth_flush_pending(cnx);
+
+ del_timer_sync(&cnx->ack_timer);
+ del_timer_sync(&cnx->reset_timer);
+
spin_lock_irq(&cnx->lock);
+
if (cnx->state & VETH_STATE_RESET)
goto restart;
+
+ /* Hack, wait for the other end to reset itself. */
+ if (! (cnx->state & VETH_STATE_SHUTDOWN)) {
+ schedule_delayed_work(&cnx->statemachine_wq, 5 * HZ);
+ goto out;
+ }
}
if (cnx->state & VETH_STATE_SHUTDOWN)
@@ -488,7 +713,7 @@ static void veth_statemachine(void *p)
if ( (cnx->state & VETH_STATE_OPEN)
&& !(cnx->state & VETH_STATE_SENTMON) ) {
- rc = veth_signalevent(cnx, VethEventTypeMonitor,
+ rc = veth_signalevent(cnx, VETH_EVENT_MONITOR,
HvLpEvent_AckInd_DoAck,
HvLpEvent_AckType_DeferredAck,
0, 0, 0, 0, 0, 0);
@@ -498,9 +723,8 @@ static void veth_statemachine(void *p)
} else {
if ( (rc != HvLpEvent_Rc_PartitionDead)
&& (rc != HvLpEvent_Rc_PathClosed) )
- veth_error("Error sending monitor to "
- "lpar %d, rc=%x\n",
- rlp, (int) rc);
+ veth_error("Error sending monitor to LPAR %d, "
+ "rc = %d\n", rlp, rc);
/* Oh well, hope we get a cap from the other
* end and do better when that kicks us */
@@ -512,7 +736,7 @@ static void veth_statemachine(void *p)
&& !(cnx->state & VETH_STATE_SENTCAPS)) {
u64 *rawcap = (u64 *)&cnx->local_caps;
- rc = veth_signalevent(cnx, VethEventTypeCap,
+ rc = veth_signalevent(cnx, VETH_EVENT_CAP,
HvLpEvent_AckInd_DoAck,
HvLpEvent_AckType_ImmediateAck,
0, rawcap[0], rawcap[1], rawcap[2],
@@ -523,9 +747,9 @@ static void veth_statemachine(void *p)
} else {
if ( (rc != HvLpEvent_Rc_PartitionDead)
&& (rc != HvLpEvent_Rc_PathClosed) )
- veth_error("Error sending caps to "
- "lpar %d, rc=%x\n",
- rlp, (int) rc);
+ veth_error("Error sending caps to LPAR %d, "
+ "rc = %d\n", rlp, rc);
+
/* Oh well, hope we get a cap from the other
* end and do better when that kicks us */
goto out;
@@ -534,7 +758,7 @@ static void veth_statemachine(void *p)
if ((cnx->state & VETH_STATE_GOTCAPS)
&& !(cnx->state & VETH_STATE_SENTCAPACK)) {
- struct VethCapData *remote_caps = &cnx->remote_caps;
+ struct veth_cap_data *remote_caps = &cnx->remote_caps;
memcpy(remote_caps, &cnx->cap_event.u.caps_data,
sizeof(*remote_caps));
@@ -565,10 +789,8 @@ static void veth_statemachine(void *p)
add_timer(&cnx->ack_timer);
cnx->state |= VETH_STATE_READY;
} else {
- veth_printk(KERN_ERR, "Caps rejected (rc=%d) by "
- "lpar %d\n",
- cnx->cap_ack_event.base_event.xRc,
- rlp);
+ veth_error("Caps rejected by LPAR %d, rc = %d\n",
+ rlp, cnx->cap_ack_event.base_event.xRc);
goto cant_cope;
}
}
@@ -581,8 +803,8 @@ static void veth_statemachine(void *p)
/* FIXME: we get here if something happens we really can't
* cope with. The link will never work once we get here, and
* all we can do is not lock the rest of the system up */
- veth_error("Badness on connection to lpar %d (state=%04lx) "
- " - shutting down\n", rlp, cnx->state);
+ veth_error("Unrecoverable error on connection to LPAR %d, shutting down"
+ " (state = 0x%04lx)\n", rlp, cnx->state);
cnx->state |= VETH_STATE_SHUTDOWN;
spin_unlock_irq(&cnx->lock);
}
@@ -591,7 +813,7 @@ static int veth_init_connection(u8 rlp)
{
struct veth_lpar_connection *cnx;
struct veth_msg *msgs;
- int i;
+ int i, rc;
if ( (rlp == this_lp)
|| ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) )
@@ -605,22 +827,36 @@ static int veth_init_connection(u8 rlp)
cnx->remote_lp = rlp;
spin_lock_init(&cnx->lock);
INIT_WORK(&cnx->statemachine_wq, veth_statemachine, cnx);
+
init_timer(&cnx->ack_timer);
cnx->ack_timer.function = veth_timed_ack;
cnx->ack_timer.data = (unsigned long) cnx;
+
+ init_timer(&cnx->reset_timer);
+ cnx->reset_timer.function = veth_timed_reset;
+ cnx->reset_timer.data = (unsigned long) cnx;
+ cnx->reset_timeout = 5 * HZ * (VETH_ACKTIMEOUT / 1000000);
+
memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks));
veth_cnx[rlp] = cnx;
+ /* This gets us 1 reference, which is held on behalf of the driver
+ * infrastructure. It's released at module unload. */
+ kobject_init(&cnx->kobject);
+ cnx->kobject.ktype = &veth_lpar_connection_ktype;
+ rc = kobject_set_name(&cnx->kobject, "cnx%.2d", rlp);
+ if (rc != 0)
+ return rc;
+
msgs = kmalloc(VETH_NUMBUFFERS * sizeof(struct veth_msg), GFP_KERNEL);
if (! msgs) {
- veth_error("Can't allocate buffers for lpar %d\n", rlp);
+ veth_error("Can't allocate buffers for LPAR %d.\n", rlp);
return -ENOMEM;
}
cnx->msgs = msgs;
memset(msgs, 0, VETH_NUMBUFFERS * sizeof(struct veth_msg));
- spin_lock_init(&cnx->msg_stack_lock);
for (i = 0; i < VETH_NUMBUFFERS; i++) {
msgs[i].token = i;
@@ -630,8 +866,7 @@ static int veth_init_connection(u8 rlp)
cnx->num_events = veth_allocate_events(rlp, 2 + VETH_NUMBUFFERS);
if (cnx->num_events < (2 + VETH_NUMBUFFERS)) {
- veth_error("Can't allocate events for lpar %d, only got %d\n",
- rlp, cnx->num_events);
+ veth_error("Can't allocate enough events for LPAR %d.\n", rlp);
return -ENOMEM;
}
@@ -642,11 +877,9 @@ static int veth_init_connection(u8 rlp)
return 0;
}
-static void veth_stop_connection(u8 rlp)
+static void veth_stop_connection(struct veth_lpar_connection *cnx)
{
- struct veth_lpar_connection *cnx = veth_cnx[rlp];
-
- if (! cnx)
+ if (!cnx)
return;
spin_lock_irq(&cnx->lock);
@@ -654,12 +887,23 @@ static void veth_stop_connection(u8 rlp)
veth_kick_statemachine(cnx);
spin_unlock_irq(&cnx->lock);
+ /* There's a slim chance the reset code has just queued the
+ * statemachine to run in five seconds. If so we need to cancel
+ * that and requeue the work to run now. */
+ if (cancel_delayed_work(&cnx->statemachine_wq)) {
+ spin_lock_irq(&cnx->lock);
+ veth_kick_statemachine(cnx);
+ spin_unlock_irq(&cnx->lock);
+ }
+
+ /* Wait for the state machine to run. */
flush_scheduled_work();
+}
- /* FIXME: not sure if this is necessary - will already have
- * been deleted by the state machine, just want to make sure
- * its not running any more */
- del_timer_sync(&cnx->ack_timer);
+static void veth_destroy_connection(struct veth_lpar_connection *cnx)
+{
+ if (!cnx)
+ return;
if (cnx->num_events > 0)
mf_deallocate_lp_events(cnx->remote_lp,
@@ -671,18 +915,18 @@ static void veth_stop_connection(u8 rlp)
HvLpEvent_Type_VirtualLan,
cnx->num_ack_events,
NULL, NULL);
-}
-
-static void veth_destroy_connection(u8 rlp)
-{
- struct veth_lpar_connection *cnx = veth_cnx[rlp];
-
- if (! cnx)
- return;
kfree(cnx->msgs);
+ veth_cnx[cnx->remote_lp] = NULL;
kfree(cnx);
- veth_cnx[rlp] = NULL;
+}
+
+static void veth_release_connection(struct kobject *kobj)
+{
+ struct veth_lpar_connection *cnx;
+ cnx = container_of(kobj, struct veth_lpar_connection, kobject);
+ veth_stop_connection(cnx);
+ veth_destroy_connection(cnx);
}
/*
@@ -726,17 +970,15 @@ static void veth_set_multicast_list(struct net_device *dev)
write_lock_irqsave(&port->mcast_gate, flags);
- if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
- printk(KERN_INFO "%s: Promiscuous mode enabled.\n",
- dev->name);
+ if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
+ (dev->mc_count > VETH_MAX_MCAST)) {
port->promiscuous = 1;
- } else if ( (dev->flags & IFF_ALLMULTI)
- || (dev->mc_count > VETH_MAX_MCAST) ) {
- port->all_mcast = 1;
} else {
struct dev_mc_list *dmi = dev->mc_list;
int i;
+ port->promiscuous = 0;
+
/* Update table */
port->num_mcast = 0;
@@ -758,9 +1000,10 @@ static void veth_set_multicast_list(struct net_device *dev)
static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
- strncpy(info->driver, "veth", sizeof(info->driver) - 1);
+ strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1);
info->driver[sizeof(info->driver) - 1] = '\0';
- strncpy(info->version, "1.0", sizeof(info->version) - 1);
+ strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1);
+ info->version[sizeof(info->version) - 1] = '\0';
}
static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
@@ -791,49 +1034,6 @@ static struct ethtool_ops ops = {
.get_link = veth_get_link,
};
-static void veth_tx_timeout(struct net_device *dev)
-{
- struct veth_port *port = (struct veth_port *)dev->priv;
- struct net_device_stats *stats = &port->stats;
- unsigned long flags;
- int i;
-
- stats->tx_errors++;
-
- spin_lock_irqsave(&port->pending_gate, flags);
-
- if (!port->pending_lpmask) {
- spin_unlock_irqrestore(&port->pending_gate, flags);
- return;
- }
-
- printk(KERN_WARNING "%s: Tx timeout! Resetting lp connections: %08x\n",
- dev->name, port->pending_lpmask);
-
- for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
- struct veth_lpar_connection *cnx = veth_cnx[i];
-
- if (! (port->pending_lpmask & (1<<i)))
- continue;
-
- /* If we're pending on it, we must be connected to it,
- * so we should certainly have a structure for it. */
- BUG_ON(! cnx);
-
- /* Theoretically we could be kicking a connection
- * which doesn't deserve it, but in practice if we've
- * had a Tx timeout, the pending_lpmask will have
- * exactly one bit set - the connection causing the
- * problem. */
- spin_lock(&cnx->lock);
- cnx->state |= VETH_STATE_RESET;
- veth_kick_statemachine(cnx);
- spin_unlock(&cnx->lock);
- }
-
- spin_unlock_irqrestore(&port->pending_gate, flags);
-}
-
static struct net_device * __init veth_probe_one(int vlan, struct device *vdev)
{
struct net_device *dev;
@@ -848,8 +1048,9 @@ static struct net_device * __init veth_probe_one(int vlan, struct device *vdev)
port = (struct veth_port *) dev->priv;
- spin_lock_init(&port->pending_gate);
+ spin_lock_init(&port->queue_lock);
rwlock_init(&port->mcast_gate);
+ port->stopped_map = 0;
for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
HvLpVirtualLanIndexMap map;
@@ -882,22 +1083,24 @@ static struct net_device * __init veth_probe_one(int vlan, struct device *vdev)
dev->set_multicast_list = veth_set_multicast_list;
SET_ETHTOOL_OPS(dev, &ops);
- dev->watchdog_timeo = 2 * (VETH_ACKTIMEOUT * HZ / 1000000);
- dev->tx_timeout = veth_tx_timeout;
-
SET_NETDEV_DEV(dev, vdev);
rc = register_netdev(dev);
if (rc != 0) {
- veth_printk(KERN_ERR,
- "Failed to register ethernet device for vlan %d\n",
- vlan);
+ veth_error("Failed registering net device for vlan%d.\n", vlan);
free_netdev(dev);
return NULL;
}
- veth_printk(KERN_DEBUG, "%s attached to iSeries vlan %d (lpar_map=0x%04x)\n",
- dev->name, vlan, port->lpar_map);
+ kobject_init(&port->kobject);
+ port->kobject.parent = &dev->class_dev.kobj;
+ port->kobject.ktype = &veth_port_ktype;
+ kobject_set_name(&port->kobject, "veth_port");
+ if (0 != kobject_add(&port->kobject))
+ veth_error("Failed adding port for %s to sysfs.\n", dev->name);
+
+ veth_info("%s attached to iSeries vlan %d (LPAR map = 0x%.4X)\n",
+ dev->name, vlan, port->lpar_map);
return dev;
}
@@ -912,98 +1115,95 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp,
struct veth_lpar_connection *cnx = veth_cnx[rlp];
struct veth_port *port = (struct veth_port *) dev->priv;
HvLpEvent_Rc rc;
- u32 dma_address, dma_length;
struct veth_msg *msg = NULL;
- int err = 0;
unsigned long flags;
- if (! cnx) {
- port->stats.tx_errors++;
- dev_kfree_skb(skb);
+ if (! cnx)
return 0;
- }
spin_lock_irqsave(&cnx->lock, flags);
if (! (cnx->state & VETH_STATE_READY))
- goto drop;
+ goto no_error;
- if ((skb->len - 14) > VETH_MAX_MTU)
+ if ((skb->len - ETH_HLEN) > VETH_MAX_MTU)
goto drop;
msg = veth_stack_pop(cnx);
-
- if (! msg) {
- err = 1;
+ if (! msg)
goto drop;
- }
- dma_length = skb->len;
- dma_address = dma_map_single(port->dev, skb->data,
- dma_length, DMA_TO_DEVICE);
+ msg->in_use = 1;
+ msg->skb = skb_get(skb);
+
+ msg->data.addr[0] = dma_map_single(port->dev, skb->data,
+ skb->len, DMA_TO_DEVICE);
- if (dma_mapping_error(dma_address))
+ if (dma_mapping_error(msg->data.addr[0]))
goto recycle_and_drop;
- /* Is it really necessary to check the length and address
- * fields of the first entry here? */
- msg->skb = skb;
msg->dev = port->dev;
- msg->data.addr[0] = dma_address;
- msg->data.len[0] = dma_length;
+ msg->data.len[0] = skb->len;
msg->data.eofmask = 1 << VETH_EOF_SHIFT;
- set_bit(0, &(msg->in_use));
- rc = veth_signaldata(cnx, VethEventTypeFrames, msg->token, &msg->data);
+
+ rc = veth_signaldata(cnx, VETH_EVENT_FRAMES, msg->token, &msg->data);
if (rc != HvLpEvent_Rc_Good)
goto recycle_and_drop;
+ /* If the timer's not already running, start it now. */
+ if (0 == cnx->outstanding_tx)
+ mod_timer(&cnx->reset_timer, jiffies + cnx->reset_timeout);
+
+ cnx->last_contact = jiffies;
+ cnx->outstanding_tx++;
+
+ if (veth_stack_is_empty(cnx))
+ veth_stop_queues(cnx);
+
+ no_error:
spin_unlock_irqrestore(&cnx->lock, flags);
return 0;
recycle_and_drop:
- msg->skb = NULL;
- /* need to set in use to make veth_recycle_msg in case this
- * was a mapping failure */
- set_bit(0, &msg->in_use);
veth_recycle_msg(cnx, msg);
drop:
- port->stats.tx_errors++;
- dev_kfree_skb(skb);
spin_unlock_irqrestore(&cnx->lock, flags);
- return err;
+ return 1;
}
-static HvLpIndexMap veth_transmit_to_many(struct sk_buff *skb,
+static void veth_transmit_to_many(struct sk_buff *skb,
HvLpIndexMap lpmask,
struct net_device *dev)
{
struct veth_port *port = (struct veth_port *) dev->priv;
- int i;
- int rc;
+ int i, success, error;
+
+ success = error = 0;
for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
if ((lpmask & (1 << i)) == 0)
continue;
- rc = veth_transmit_to_one(skb_get(skb), i, dev);
- if (! rc)
- lpmask &= ~(1<<i);
+ if (veth_transmit_to_one(skb, i, dev))
+ error = 1;
+ else
+ success = 1;
}
- if (! lpmask) {
+ if (error)
+ port->stats.tx_errors++;
+
+ if (success) {
port->stats.tx_packets++;
port->stats.tx_bytes += skb->len;
}
-
- return lpmask;
}
static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
unsigned char *frame = skb->data;
struct veth_port *port = (struct veth_port *) dev->priv;
- unsigned long flags;
HvLpIndexMap lpmask;
if (! (frame[0] & 0x01)) {
@@ -1020,44 +1220,27 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev)
lpmask = port->lpar_map;
}
- spin_lock_irqsave(&port->pending_gate, flags);
-
- lpmask = veth_transmit_to_many(skb, lpmask, dev);
-
- dev->trans_start = jiffies;
+ veth_transmit_to_many(skb, lpmask, dev);
- if (! lpmask) {
- dev_kfree_skb(skb);
- } else {
- if (port->pending_skb) {
- veth_error("%s: Tx while skb was pending!\n",
- dev->name);
- dev_kfree_skb(skb);
- spin_unlock_irqrestore(&port->pending_gate, flags);
- return 1;
- }
-
- port->pending_skb = skb;
- port->pending_lpmask = lpmask;
- netif_stop_queue(dev);
- }
-
- spin_unlock_irqrestore(&port->pending_gate, flags);
+ dev_kfree_skb(skb);
return 0;
}
+/* You must hold the connection's lock when you call this function. */
static void veth_recycle_msg(struct veth_lpar_connection *cnx,
struct veth_msg *msg)
{
u32 dma_address, dma_length;
- if (test_and_clear_bit(0, &msg->in_use)) {
+ if (msg->in_use) {
+ msg->in_use = 0;
dma_address = msg->data.addr[0];
dma_length = msg->data.len[0];
- dma_unmap_single(msg->dev, dma_address, dma_length,
- DMA_TO_DEVICE);
+ if (!dma_mapping_error(dma_address))
+ dma_unmap_single(msg->dev, dma_address, dma_length,
+ DMA_TO_DEVICE);
if (msg->skb) {
dev_kfree_skb_any(msg->skb);
@@ -1066,15 +1249,16 @@ static void veth_recycle_msg(struct veth_lpar_connection *cnx,
memset(&msg->data, 0, sizeof(msg->data));
veth_stack_push(cnx, msg);
- } else
- if (cnx->state & VETH_STATE_OPEN)
- veth_error("Bogus frames ack from lpar %d (#%d)\n",
- cnx->remote_lp, msg->token);
+ } else if (cnx->state & VETH_STATE_OPEN) {
+ veth_error("Non-pending frame (# %d) acked by LPAR %d.\n",
+ cnx->remote_lp, msg->token);
+ }
}
-static void veth_flush_pending(struct veth_lpar_connection *cnx)
+static void veth_wake_queues(struct veth_lpar_connection *cnx)
{
int i;
+
for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) {
struct net_device *dev = veth_dev[i];
struct veth_port *port;
@@ -1088,20 +1272,77 @@ static void veth_flush_pending(struct veth_lpar_connection *cnx)
if (! (port->lpar_map & (1<<cnx->remote_lp)))
continue;
- spin_lock_irqsave(&port->pending_gate, flags);
- if (port->pending_skb) {
- port->pending_lpmask =
- veth_transmit_to_many(port->pending_skb,
- port->pending_lpmask,
- dev);
- if (! port->pending_lpmask) {
- dev_kfree_skb_any(port->pending_skb);
- port->pending_skb = NULL;
- netif_wake_queue(dev);
- }
+ spin_lock_irqsave(&port->queue_lock, flags);
+
+ port->stopped_map &= ~(1 << cnx->remote_lp);
+
+ if (0 == port->stopped_map && netif_queue_stopped(dev)) {
+ veth_debug("cnx %d: woke queue for %s.\n",
+ cnx->remote_lp, dev->name);
+ netif_wake_queue(dev);
+ }
+ spin_unlock_irqrestore(&port->queue_lock, flags);
+ }
+}
+
+static void veth_stop_queues(struct veth_lpar_connection *cnx)
+{
+ int i;
+
+ for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) {
+ struct net_device *dev = veth_dev[i];
+ struct veth_port *port;
+
+ if (! dev)
+ continue;
+
+ port = (struct veth_port *)dev->priv;
+
+ /* If this cnx is not on the vlan for this port, continue */
+ if (! (port->lpar_map & (1 << cnx->remote_lp)))
+ continue;
+
+ spin_lock(&port->queue_lock);
+
+ netif_stop_queue(dev);
+ port->stopped_map |= (1 << cnx->remote_lp);
+
+ veth_debug("cnx %d: stopped queue for %s, map = 0x%x.\n",
+ cnx->remote_lp, dev->name, port->stopped_map);
+
+ spin_unlock(&port->queue_lock);
+ }
+}
+
+static void veth_timed_reset(unsigned long ptr)
+{
+ struct veth_lpar_connection *cnx = (struct veth_lpar_connection *)ptr;
+ unsigned long trigger_time, flags;
+
+ /* FIXME is it possible this fires after veth_stop_connection()?
+ * That would reschedule the statemachine for 5 seconds and probably
+ * execute it after the module's been unloaded. Hmm. */
+
+ spin_lock_irqsave(&cnx->lock, flags);
+
+ if (cnx->outstanding_tx > 0) {
+ trigger_time = cnx->last_contact + cnx->reset_timeout;
+
+ if (trigger_time < jiffies) {
+ cnx->state |= VETH_STATE_RESET;
+ veth_kick_statemachine(cnx);
+ veth_error("%d packets not acked by LPAR %d within %d "
+ "seconds, resetting.\n",
+ cnx->outstanding_tx, cnx->remote_lp,
+ cnx->reset_timeout / HZ);
+ } else {
+ /* Reschedule the timer */
+ trigger_time = jiffies + cnx->reset_timeout;
+ mod_timer(&cnx->reset_timer, trigger_time);
}
- spin_unlock_irqrestore(&port->pending_gate, flags);
}
+
+ spin_unlock_irqrestore(&cnx->lock, flags);
}
/*
@@ -1117,12 +1358,9 @@ static inline int veth_frame_wanted(struct veth_port *port, u64 mac_addr)
if ( (mac_addr == port->mac_addr) || (mac_addr == 0xffffffffffff0000) )
return 1;
- if (! (((char *) &mac_addr)[0] & 0x01))
- return 0;
-
read_lock_irqsave(&port->mcast_gate, flags);
- if (port->promiscuous || port->all_mcast) {
+ if (port->promiscuous) {
wanted = 1;
goto out;
}
@@ -1175,21 +1413,21 @@ static void veth_flush_acks(struct veth_lpar_connection *cnx)
{
HvLpEvent_Rc rc;
- rc = veth_signaldata(cnx, VethEventTypeFramesAck,
+ rc = veth_signaldata(cnx, VETH_EVENT_FRAMES_ACK,
0, &cnx->pending_acks);
if (rc != HvLpEvent_Rc_Good)
- veth_error("Error 0x%x acking frames from lpar %d!\n",
- (unsigned)rc, cnx->remote_lp);
+ veth_error("Failed acking frames from LPAR %d, rc = %d\n",
+ cnx->remote_lp, (int)rc);
cnx->num_pending_acks = 0;
memset(&cnx->pending_acks, 0xff, sizeof(cnx->pending_acks));
}
static void veth_receive(struct veth_lpar_connection *cnx,
- struct VethLpEvent *event)
+ struct veth_lpevent *event)
{
- struct VethFramesData *senddata = &event->u.frames_data;
+ struct veth_frames_data *senddata = &event->u.frames_data;
int startchunk = 0;
int nchunks;
unsigned long flags;
@@ -1216,9 +1454,10 @@ static void veth_receive(struct veth_lpar_connection *cnx,
/* make sure that we have at least 1 EOF entry in the
* remaining entries */
if (! (senddata->eofmask >> (startchunk + VETH_EOF_SHIFT))) {
- veth_error("missing EOF frag in event "
- "eofmask=0x%x startchunk=%d\n",
- (unsigned) senddata->eofmask, startchunk);
+ veth_error("Missing EOF fragment in event "
+ "eofmask = 0x%x startchunk = %d\n",
+ (unsigned)senddata->eofmask,
+ startchunk);
break;
}
@@ -1237,8 +1476,9 @@ static void veth_receive(struct veth_lpar_connection *cnx,
/* nchunks == # of chunks in this frame */
if ((length - ETH_HLEN) > VETH_MAX_MTU) {
- veth_error("Received oversize frame from lpar %d "
- "(length=%d)\n", cnx->remote_lp, length);
+ veth_error("Received oversize frame from LPAR %d "
+ "(length = %d)\n",
+ cnx->remote_lp, length);
continue;
}
@@ -1331,15 +1571,33 @@ static void veth_timed_ack(unsigned long ptr)
static int veth_remove(struct vio_dev *vdev)
{
- int i = vdev->unit_address;
+ struct veth_lpar_connection *cnx;
struct net_device *dev;
+ struct veth_port *port;
+ int i;
- dev = veth_dev[i];
- if (dev != NULL) {
- veth_dev[i] = NULL;
- unregister_netdev(dev);
- free_netdev(dev);
+ dev = veth_dev[vdev->unit_address];
+
+ if (! dev)
+ return 0;
+
+ port = netdev_priv(dev);
+
+ for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
+ cnx = veth_cnx[i];
+
+ if (cnx && (port->lpar_map & (1 << i))) {
+ /* Drop our reference to connections on our VLAN */
+ kobject_put(&cnx->kobject);
+ }
}
+
+ veth_dev[vdev->unit_address] = NULL;
+ kobject_del(&port->kobject);
+ kobject_put(&port->kobject);
+ unregister_netdev(dev);
+ free_netdev(dev);
+
return 0;
}
@@ -1347,6 +1605,7 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id)
{
int i = vdev->unit_address;
struct net_device *dev;
+ struct veth_port *port;
dev = veth_probe_one(i, &vdev->dev);
if (dev == NULL) {
@@ -1355,11 +1614,23 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id)
}
veth_dev[i] = dev;
- /* Start the state machine on each connection, to commence
- * link negotiation */
- for (i = 0; i < HVMAXARCHITECTEDLPS; i++)
- if (veth_cnx[i])
- veth_kick_statemachine(veth_cnx[i]);
+ port = (struct veth_port*)netdev_priv(dev);
+
+ /* Start the state machine on each connection on this vlan. If we're
+ * the first dev to do so this will commence link negotiation */
+ for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
+ struct veth_lpar_connection *cnx;
+
+ if (! (port->lpar_map & (1 << i)))
+ continue;
+
+ cnx = veth_cnx[i];
+ if (!cnx)
+ continue;
+
+ kobject_get(&cnx->kobject);
+ veth_kick_statemachine(cnx);
+ }
return 0;
}
@@ -1370,12 +1641,12 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id)
*/
static struct vio_device_id veth_device_table[] __devinitdata = {
{ "vlan", "" },
- { NULL, NULL }
+ { "", "" }
};
MODULE_DEVICE_TABLE(vio, veth_device_table);
static struct vio_driver veth_driver = {
- .name = "iseries_veth",
+ .name = DRV_NAME,
.id_table = veth_device_table,
.probe = veth_probe,
.remove = veth_remove
@@ -1388,29 +1659,29 @@ static struct vio_driver veth_driver = {
void __exit veth_module_cleanup(void)
{
int i;
+ struct veth_lpar_connection *cnx;
- /* Stop the queues first to stop any new packets being sent. */
- for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++)
- if (veth_dev[i])
- netif_stop_queue(veth_dev[i]);
-
- /* Stop the connections before we unregister the driver. This
- * ensures there's no skbs lying around holding the device open. */
- for (i = 0; i < HVMAXARCHITECTEDLPS; ++i)
- veth_stop_connection(i);
-
+ /* Disconnect our "irq" to stop events coming from the Hypervisor. */
HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan);
- /* Hypervisor callbacks may have scheduled more work while we
- * were stoping connections. Now that we've disconnected from
- * the hypervisor make sure everything's finished. */
+ /* Make sure any work queued from Hypervisor callbacks is finished. */
flush_scheduled_work();
- vio_unregister_driver(&veth_driver);
+ for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) {
+ cnx = veth_cnx[i];
+
+ if (!cnx)
+ continue;
- for (i = 0; i < HVMAXARCHITECTEDLPS; ++i)
- veth_destroy_connection(i);
+ /* Remove the connection from sysfs */
+ kobject_del(&cnx->kobject);
+ /* Drop the driver's reference to the connection */
+ kobject_put(&cnx->kobject);
+ }
+ /* Unregister the driver, which will close all the netdevs and stop
+ * the connections when they're no longer referenced. */
+ vio_unregister_driver(&veth_driver);
}
module_exit(veth_module_cleanup);
@@ -1423,15 +1694,37 @@ int __init veth_module_init(void)
for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) {
rc = veth_init_connection(i);
- if (rc != 0) {
- veth_module_cleanup();
- return rc;
- }
+ if (rc != 0)
+ goto error;
}
HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan,
&veth_handle_event);
- return vio_register_driver(&veth_driver);
+ rc = vio_register_driver(&veth_driver);
+ if (rc != 0)
+ goto error;
+
+ for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) {
+ struct kobject *kobj;
+
+ if (!veth_cnx[i])
+ continue;
+
+ kobj = &veth_cnx[i]->kobject;
+ kobj->parent = &veth_driver.driver.kobj;
+ /* If the add failes, complain but otherwise continue */
+ if (0 != kobject_add(kobj))
+ veth_error("cnx %d: Failed adding to sysfs.\n", i);
+ }
+
+ return 0;
+
+error:
+ for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) {
+ veth_destroy_connection(veth_cnx[i]);
+ }
+
+ return rc;
}
module_init(veth_module_init);
diff --git a/drivers/net/iseries_veth.h b/drivers/net/iseries_veth.h
deleted file mode 100644
index d9370f79b83e..000000000000
--- a/drivers/net/iseries_veth.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* File veth.h created by Kyle A. Lucke on Mon Aug 7 2000. */
-
-#ifndef _ISERIES_VETH_H
-#define _ISERIES_VETH_H
-
-#define VethEventTypeCap (0)
-#define VethEventTypeFrames (1)
-#define VethEventTypeMonitor (2)
-#define VethEventTypeFramesAck (3)
-
-#define VETH_MAX_ACKS_PER_MSG (20)
-#define VETH_MAX_FRAMES_PER_MSG (6)
-
-struct VethFramesData {
- u32 addr[VETH_MAX_FRAMES_PER_MSG];
- u16 len[VETH_MAX_FRAMES_PER_MSG];
- u32 eofmask;
-};
-#define VETH_EOF_SHIFT (32-VETH_MAX_FRAMES_PER_MSG)
-
-struct VethFramesAckData {
- u16 token[VETH_MAX_ACKS_PER_MSG];
-};
-
-struct VethCapData {
- u8 caps_version;
- u8 rsvd1;
- u16 num_buffers;
- u16 ack_threshold;
- u16 rsvd2;
- u32 ack_timeout;
- u32 rsvd3;
- u64 rsvd4[3];
-};
-
-struct VethLpEvent {
- struct HvLpEvent base_event;
- union {
- struct VethCapData caps_data;
- struct VethFramesData frames_data;
- struct VethFramesAckData frames_ack_data;
- } u;
-
-};
-
-#endif /* _ISERIES_VETH_H */
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index a32668e88e09..bb71638a7c44 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -1657,7 +1657,6 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
skb->dev = ppp->dev;
skb->protocol = htons(npindex_to_ethertype[npi]);
skb->mac.raw = skb->data;
- skb->input_dev = ppp->dev;
netif_rx(skb);
ppp->dev->last_rx = jiffies;
}
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index ce1a9bf7b9a7..82f236cc3b9b 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -377,7 +377,8 @@ abort_kfree:
***********************************************************************/
static int pppoe_rcv(struct sk_buff *skb,
struct net_device *dev,
- struct packet_type *pt)
+ struct packet_type *pt,
+ struct net_device *orig_dev)
{
struct pppoe_hdr *ph;
@@ -426,7 +427,8 @@ out:
***********************************************************************/
static int pppoe_disc_rcv(struct sk_buff *skb,
struct net_device *dev,
- struct packet_type *pt)
+ struct packet_type *pt,
+ struct net_device *orig_dev)
{
struct pppoe_hdr *ph;
diff --git a/drivers/net/rrunner.c b/drivers/net/rrunner.c
index 12a86f96d973..ec1a18d189a1 100644
--- a/drivers/net/rrunner.c
+++ b/drivers/net/rrunner.c
@@ -1429,6 +1429,7 @@ static int rr_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rr_private *rrpriv = netdev_priv(dev);
struct rr_regs __iomem *regs = rrpriv->regs;
+ struct hippi_cb *hcb = (struct hippi_cb *) skb->cb;
struct ring_ctrl *txctrl;
unsigned long flags;
u32 index, len = skb->len;
@@ -1460,7 +1461,7 @@ static int rr_start_xmit(struct sk_buff *skb, struct net_device *dev)
ifield = (u32 *)skb_push(skb, 8);
ifield[0] = 0;
- ifield[1] = skb->private.ifield;
+ ifield[1] = hcb->ifield;
/*
* We don't need the lock before we are actually going to start
diff --git a/drivers/net/s2io.h b/drivers/net/s2io.h
index 5d9270730ca2..bc64d967f080 100644
--- a/drivers/net/s2io.h
+++ b/drivers/net/s2io.h
@@ -762,8 +762,8 @@ static inline u64 readq(void __iomem *addr)
{
u64 ret = 0;
ret = readl(addr + 4);
- (u64) ret <<= 32;
- (u64) ret |= readl(addr);
+ ret <<= 32;
+ ret |= readl(addr);
return ret;
}
diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c
index 3ad0b6751f6f..221354eea21f 100644
--- a/drivers/net/shaper.c
+++ b/drivers/net/shaper.c
@@ -156,52 +156,6 @@ static int shaper_start_xmit(struct sk_buff *skb, struct net_device *dev)
SHAPERCB(skb)->shapelen= shaper_clocks(shaper,skb);
-#ifdef SHAPER_COMPLEX /* and broken.. */
-
- while(ptr && ptr!=(struct sk_buff *)&shaper->sendq)
- {
- if(ptr->pri<skb->pri
- && jiffies - SHAPERCB(ptr)->shapeclock < SHAPER_MAXSLIP)
- {
- struct sk_buff *tmp=ptr->prev;
-
- /*
- * It goes before us therefore we slip the length
- * of the new frame.
- */
-
- SHAPERCB(ptr)->shapeclock+=SHAPERCB(skb)->shapelen;
- SHAPERCB(ptr)->shapelatency+=SHAPERCB(skb)->shapelen;
-
- /*
- * The packet may have slipped so far back it
- * fell off.
- */
- if(SHAPERCB(ptr)->shapelatency > SHAPER_LATENCY)
- {
- skb_unlink(ptr);
- dev_kfree_skb(ptr);
- }
- ptr=tmp;
- }
- else
- break;
- }
- if(ptr==NULL || ptr==(struct sk_buff *)&shaper->sendq)
- skb_queue_head(&shaper->sendq,skb);
- else
- {
- struct sk_buff *tmp;
- /*
- * Set the packet clock out time according to the
- * frames ahead. Im sure a bit of thought could drop
- * this loop.
- */
- for(tmp=skb_peek(&shaper->sendq); tmp!=NULL && tmp!=ptr; tmp=tmp->next)
- SHAPERCB(skb)->shapeclock+=tmp->shapelen;
- skb_append(ptr,skb);
- }
-#else
{
struct sk_buff *tmp;
/*
@@ -220,7 +174,7 @@ static int shaper_start_xmit(struct sk_buff *skb, struct net_device *dev)
} else
skb_queue_tail(&shaper->sendq, skb);
}
-#endif
+
if(sh_debug)
printk("Frame queued.\n");
if(skb_queue_len(&shaper->sendq)>SHAPER_QLEN)
@@ -302,7 +256,7 @@ static void shaper_kick(struct shaper *shaper)
* Pull the frame and get interrupts back on.
*/
- skb_unlink(skb);
+ skb_unlink(skb, &shaper->sendq);
if (shaper->recovery <
SHAPERCB(skb)->shapeclock + SHAPERCB(skb)->shapelen)
shaper->recovery = SHAPERCB(skb)->shapeclock + SHAPERCB(skb)->shapelen;
diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c
new file mode 100644
index 000000000000..bf3440aa6c24
--- /dev/null
+++ b/drivers/net/sis190.c
@@ -0,0 +1,1843 @@
+/*
+ sis190.c: Silicon Integrated Systems SiS190 ethernet driver
+
+ Copyright (c) 2003 K.M. Liu <kmliu@sis.com>
+ Copyright (c) 2003, 2004 Jeff Garzik <jgarzik@pobox.com>
+ Copyright (c) 2003, 2004, 2005 Francois Romieu <romieu@fr.zoreil.com>
+
+ Based on r8169.c, tg3.c, 8139cp.c, skge.c, epic100.c and SiS 190/191
+ genuine driver.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ See the file COPYING in this distribution for more information.
+
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/pci.h>
+#include <linux/mii.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/dma-mapping.h>
+#include <asm/irq.h>
+
+#define net_drv(p, arg...) if (netif_msg_drv(p)) \
+ printk(arg)
+#define net_probe(p, arg...) if (netif_msg_probe(p)) \
+ printk(arg)
+#define net_link(p, arg...) if (netif_msg_link(p)) \
+ printk(arg)
+#define net_intr(p, arg...) if (netif_msg_intr(p)) \
+ printk(arg)
+#define net_tx_err(p, arg...) if (netif_msg_tx_err(p)) \
+ printk(arg)
+
+#define PHY_MAX_ADDR 32
+#define PHY_ID_ANY 0x1f
+#define MII_REG_ANY 0x1f
+
+#ifdef CONFIG_SIS190_NAPI
+#define NAPI_SUFFIX "-NAPI"
+#else
+#define NAPI_SUFFIX ""
+#endif
+
+#define DRV_VERSION "1.2" NAPI_SUFFIX
+#define DRV_NAME "sis190"
+#define SIS190_DRIVER_NAME DRV_NAME " Gigabit Ethernet driver " DRV_VERSION
+#define PFX DRV_NAME ": "
+
+#ifdef CONFIG_SIS190_NAPI
+#define sis190_rx_skb netif_receive_skb
+#define sis190_rx_quota(count, quota) min(count, quota)
+#else
+#define sis190_rx_skb netif_rx
+#define sis190_rx_quota(count, quota) count
+#endif
+
+#define MAC_ADDR_LEN 6
+
+#define NUM_TX_DESC 64 /* [8..1024] */
+#define NUM_RX_DESC 64 /* [8..8192] */
+#define TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc))
+#define RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc))
+#define RX_BUF_SIZE 1536
+#define RX_BUF_MASK 0xfff8
+
+#define SIS190_REGS_SIZE 0x80
+#define SIS190_TX_TIMEOUT (6*HZ)
+#define SIS190_PHY_TIMEOUT (10*HZ)
+#define SIS190_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK | NETIF_MSG_IFUP | \
+ NETIF_MSG_IFDOWN)
+
+/* Enhanced PHY access register bit definitions */
+#define EhnMIIread 0x0000
+#define EhnMIIwrite 0x0020
+#define EhnMIIdataShift 16
+#define EhnMIIpmdShift 6 /* 7016 only */
+#define EhnMIIregShift 11
+#define EhnMIIreq 0x0010
+#define EhnMIInotDone 0x0010
+
+/* Write/read MMIO register */
+#define SIS_W8(reg, val) writeb ((val), ioaddr + (reg))
+#define SIS_W16(reg, val) writew ((val), ioaddr + (reg))
+#define SIS_W32(reg, val) writel ((val), ioaddr + (reg))
+#define SIS_R8(reg) readb (ioaddr + (reg))
+#define SIS_R16(reg) readw (ioaddr + (reg))
+#define SIS_R32(reg) readl (ioaddr + (reg))
+
+#define SIS_PCI_COMMIT() SIS_R32(IntrControl)
+
+enum sis190_registers {
+ TxControl = 0x00,
+ TxDescStartAddr = 0x04,
+ rsv0 = 0x08, // reserved
+ TxSts = 0x0c, // unused (Control/Status)
+ RxControl = 0x10,
+ RxDescStartAddr = 0x14,
+ rsv1 = 0x18, // reserved
+ RxSts = 0x1c, // unused
+ IntrStatus = 0x20,
+ IntrMask = 0x24,
+ IntrControl = 0x28,
+ IntrTimer = 0x2c, // unused (Interupt Timer)
+ PMControl = 0x30, // unused (Power Mgmt Control/Status)
+ rsv2 = 0x34, // reserved
+ ROMControl = 0x38,
+ ROMInterface = 0x3c,
+ StationControl = 0x40,
+ GMIIControl = 0x44,
+ GIoCR = 0x48, // unused (GMAC IO Compensation)
+ GIoCtrl = 0x4c, // unused (GMAC IO Control)
+ TxMacControl = 0x50,
+ TxLimit = 0x54, // unused (Tx MAC Timer/TryLimit)
+ RGDelay = 0x58, // unused (RGMII Tx Internal Delay)
+ rsv3 = 0x5c, // reserved
+ RxMacControl = 0x60,
+ RxMacAddr = 0x62,
+ RxHashTable = 0x68,
+ // Undocumented = 0x6c,
+ RxWolCtrl = 0x70,
+ RxWolData = 0x74, // unused (Rx WOL Data Access)
+ RxMPSControl = 0x78, // unused (Rx MPS Control)
+ rsv4 = 0x7c, // reserved
+};
+
+enum sis190_register_content {
+ /* IntrStatus */
+ SoftInt = 0x40000000, // unused
+ Timeup = 0x20000000, // unused
+ PauseFrame = 0x00080000, // unused
+ MagicPacket = 0x00040000, // unused
+ WakeupFrame = 0x00020000, // unused
+ LinkChange = 0x00010000,
+ RxQEmpty = 0x00000080,
+ RxQInt = 0x00000040,
+ TxQ1Empty = 0x00000020, // unused
+ TxQ1Int = 0x00000010,
+ TxQ0Empty = 0x00000008, // unused
+ TxQ0Int = 0x00000004,
+ RxHalt = 0x00000002,
+ TxHalt = 0x00000001,
+
+ /* {Rx/Tx}CmdBits */
+ CmdReset = 0x10,
+ CmdRxEnb = 0x08, // unused
+ CmdTxEnb = 0x01,
+ RxBufEmpty = 0x01, // unused
+
+ /* Cfg9346Bits */
+ Cfg9346_Lock = 0x00, // unused
+ Cfg9346_Unlock = 0xc0, // unused
+
+ /* RxMacControl */
+ AcceptErr = 0x20, // unused
+ AcceptRunt = 0x10, // unused
+ AcceptBroadcast = 0x0800,
+ AcceptMulticast = 0x0400,
+ AcceptMyPhys = 0x0200,
+ AcceptAllPhys = 0x0100,
+
+ /* RxConfigBits */
+ RxCfgFIFOShift = 13,
+ RxCfgDMAShift = 8, // 0x1a in RxControl ?
+
+ /* TxConfigBits */
+ TxInterFrameGapShift = 24,
+ TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */
+
+ /* StationControl */
+ _1000bpsF = 0x1c00,
+ _1000bpsH = 0x0c00,
+ _100bpsF = 0x1800,
+ _100bpsH = 0x0800,
+ _10bpsF = 0x1400,
+ _10bpsH = 0x0400,
+
+ LinkStatus = 0x02, // unused
+ FullDup = 0x01, // unused
+
+ /* TBICSRBit */
+ TBILinkOK = 0x02000000, // unused
+};
+
+struct TxDesc {
+ __le32 PSize;
+ __le32 status;
+ __le32 addr;
+ __le32 size;
+};
+
+struct RxDesc {
+ __le32 PSize;
+ __le32 status;
+ __le32 addr;
+ __le32 size;
+};
+
+enum _DescStatusBit {
+ /* _Desc.status */
+ OWNbit = 0x80000000, // RXOWN/TXOWN
+ INTbit = 0x40000000, // RXINT/TXINT
+ CRCbit = 0x00020000, // CRCOFF/CRCEN
+ PADbit = 0x00010000, // PREADD/PADEN
+ /* _Desc.size */
+ RingEnd = 0x80000000,
+ /* TxDesc.status */
+ LSEN = 0x08000000, // TSO ? -- FR
+ IPCS = 0x04000000,
+ TCPCS = 0x02000000,
+ UDPCS = 0x01000000,
+ BSTEN = 0x00800000,
+ EXTEN = 0x00400000,
+ DEFEN = 0x00200000,
+ BKFEN = 0x00100000,
+ CRSEN = 0x00080000,
+ COLEN = 0x00040000,
+ THOL3 = 0x30000000,
+ THOL2 = 0x20000000,
+ THOL1 = 0x10000000,
+ THOL0 = 0x00000000,
+ /* RxDesc.status */
+ IPON = 0x20000000,
+ TCPON = 0x10000000,
+ UDPON = 0x08000000,
+ Wakup = 0x00400000,
+ Magic = 0x00200000,
+ Pause = 0x00100000,
+ DEFbit = 0x00200000,
+ BCAST = 0x000c0000,
+ MCAST = 0x00080000,
+ UCAST = 0x00040000,
+ /* RxDesc.PSize */
+ TAGON = 0x80000000,
+ RxDescCountMask = 0x7f000000, // multi-desc pkt when > 1 ? -- FR
+ ABORT = 0x00800000,
+ SHORT = 0x00400000,
+ LIMIT = 0x00200000,
+ MIIER = 0x00100000,
+ OVRUN = 0x00080000,
+ NIBON = 0x00040000,
+ COLON = 0x00020000,
+ CRCOK = 0x00010000,
+ RxSizeMask = 0x0000ffff
+ /*
+ * The asic could apparently do vlan, TSO, jumbo (sis191 only) and
+ * provide two (unused with Linux) Tx queues. No publically
+ * available documentation alas.
+ */
+};
+
+enum sis190_eeprom_access_register_bits {
+ EECS = 0x00000001, // unused
+ EECLK = 0x00000002, // unused
+ EEDO = 0x00000008, // unused
+ EEDI = 0x00000004, // unused
+ EEREQ = 0x00000080,
+ EEROP = 0x00000200,
+ EEWOP = 0x00000100 // unused
+};
+
+/* EEPROM Addresses */
+enum sis190_eeprom_address {
+ EEPROMSignature = 0x00,
+ EEPROMCLK = 0x01, // unused
+ EEPROMInfo = 0x02,
+ EEPROMMACAddr = 0x03
+};
+
+struct sis190_private {
+ void __iomem *mmio_addr;
+ struct pci_dev *pci_dev;
+ struct net_device_stats stats;
+ spinlock_t lock;
+ u32 rx_buf_sz;
+ u32 cur_rx;
+ u32 cur_tx;
+ u32 dirty_rx;
+ u32 dirty_tx;
+ dma_addr_t rx_dma;
+ dma_addr_t tx_dma;
+ struct RxDesc *RxDescRing;
+ struct TxDesc *TxDescRing;
+ struct sk_buff *Rx_skbuff[NUM_RX_DESC];
+ struct sk_buff *Tx_skbuff[NUM_TX_DESC];
+ struct work_struct phy_task;
+ struct timer_list timer;
+ u32 msg_enable;
+ struct mii_if_info mii_if;
+ struct list_head first_phy;
+};
+
+struct sis190_phy {
+ struct list_head list;
+ int phy_id;
+ u16 id[2];
+ u16 status;
+ u8 type;
+};
+
+enum sis190_phy_type {
+ UNKNOWN = 0x00,
+ HOME = 0x01,
+ LAN = 0x02,
+ MIX = 0x03
+};
+
+static struct mii_chip_info {
+ const char *name;
+ u16 id[2];
+ unsigned int type;
+} mii_chip_table[] = {
+ { "Broadcom PHY BCM5461", { 0x0020, 0x60c0 }, LAN },
+ { "Agere PHY ET1101B", { 0x0282, 0xf010 }, LAN },
+ { "Marvell PHY 88E1111", { 0x0141, 0x0cc0 }, LAN },
+ { "Realtek PHY RTL8201", { 0x0000, 0x8200 }, LAN },
+ { NULL, }
+};
+
+const static struct {
+ const char *name;
+ u8 version; /* depend on docs */
+ u32 RxConfigMask; /* clear the bits supported by this chip */
+} sis_chip_info[] = {
+ { DRV_NAME, 0x00, 0xff7e1880, },
+};
+
+static struct pci_device_id sis190_pci_tbl[] __devinitdata = {
+ { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x0190), 0, 0, 0 },
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, sis190_pci_tbl);
+
+static int rx_copybreak = 200;
+
+static struct {
+ u32 msg_enable;
+} debug = { -1 };
+
+MODULE_DESCRIPTION("SiS sis190 Gigabit Ethernet driver");
+module_param(rx_copybreak, int, 0);
+MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames");
+module_param_named(debug, debug.msg_enable, int, 0);
+MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 16=all)");
+MODULE_AUTHOR("K.M. Liu <kmliu@sis.com>, Ueimor <romieu@fr.zoreil.com>");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+
+static const u32 sis190_intr_mask =
+ RxQEmpty | RxQInt | TxQ1Int | TxQ0Int | RxHalt | TxHalt;
+
+/*
+ * Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+ * The chips use a 64 element hash table based on the Ethernet CRC.
+ */
+static int multicast_filter_limit = 32;
+
+static void __mdio_cmd(void __iomem *ioaddr, u32 ctl)
+{
+ unsigned int i;
+
+ SIS_W32(GMIIControl, ctl);
+
+ msleep(1);
+
+ for (i = 0; i < 100; i++) {
+ if (!(SIS_R32(GMIIControl) & EhnMIInotDone))
+ break;
+ msleep(1);
+ }
+
+ if (i > 999)
+ printk(KERN_ERR PFX "PHY command failed !\n");
+}
+
+static void mdio_write(void __iomem *ioaddr, int phy_id, int reg, int val)
+{
+ __mdio_cmd(ioaddr, EhnMIIreq | EhnMIIwrite |
+ (((u32) reg) << EhnMIIregShift) | (phy_id << EhnMIIpmdShift) |
+ (((u32) val) << EhnMIIdataShift));
+}
+
+static int mdio_read(void __iomem *ioaddr, int phy_id, int reg)
+{
+ __mdio_cmd(ioaddr, EhnMIIreq | EhnMIIread |
+ (((u32) reg) << EhnMIIregShift) | (phy_id << EhnMIIpmdShift));
+
+ return (u16) (SIS_R32(GMIIControl) >> EhnMIIdataShift);
+}
+
+static void __mdio_write(struct net_device *dev, int phy_id, int reg, int val)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ mdio_write(tp->mmio_addr, phy_id, reg, val);
+}
+
+static int __mdio_read(struct net_device *dev, int phy_id, int reg)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ return mdio_read(tp->mmio_addr, phy_id, reg);
+}
+
+static u16 mdio_read_latched(void __iomem *ioaddr, int phy_id, int reg)
+{
+ mdio_read(ioaddr, phy_id, reg);
+ return mdio_read(ioaddr, phy_id, reg);
+}
+
+static u16 __devinit sis190_read_eeprom(void __iomem *ioaddr, u32 reg)
+{
+ u16 data = 0xffff;
+ unsigned int i;
+
+ if (!(SIS_R32(ROMControl) & 0x0002))
+ return 0;
+
+ SIS_W32(ROMInterface, EEREQ | EEROP | (reg << 10));
+
+ for (i = 0; i < 200; i++) {
+ if (!(SIS_R32(ROMInterface) & EEREQ)) {
+ data = (SIS_R32(ROMInterface) & 0xffff0000) >> 16;
+ break;
+ }
+ msleep(1);
+ }
+
+ return data;
+}
+
+static void sis190_irq_mask_and_ack(void __iomem *ioaddr)
+{
+ SIS_W32(IntrMask, 0x00);
+ SIS_W32(IntrStatus, 0xffffffff);
+ SIS_PCI_COMMIT();
+}
+
+static void sis190_asic_down(void __iomem *ioaddr)
+{
+ /* Stop the chip's Tx and Rx DMA processes. */
+
+ SIS_W32(TxControl, 0x1a00);
+ SIS_W32(RxControl, 0x1a00);
+
+ sis190_irq_mask_and_ack(ioaddr);
+}
+
+static void sis190_mark_as_last_descriptor(struct RxDesc *desc)
+{
+ desc->size |= cpu_to_le32(RingEnd);
+}
+
+static inline void sis190_give_to_asic(struct RxDesc *desc, u32 rx_buf_sz)
+{
+ u32 eor = le32_to_cpu(desc->size) & RingEnd;
+
+ desc->PSize = 0x0;
+ desc->size = cpu_to_le32((rx_buf_sz & RX_BUF_MASK) | eor);
+ wmb();
+ desc->status = cpu_to_le32(OWNbit | INTbit);
+}
+
+static inline void sis190_map_to_asic(struct RxDesc *desc, dma_addr_t mapping,
+ u32 rx_buf_sz)
+{
+ desc->addr = cpu_to_le32(mapping);
+ sis190_give_to_asic(desc, rx_buf_sz);
+}
+
+static inline void sis190_make_unusable_by_asic(struct RxDesc *desc)
+{
+ desc->PSize = 0x0;
+ desc->addr = 0xdeadbeef;
+ desc->size &= cpu_to_le32(RingEnd);
+ wmb();
+ desc->status = 0x0;
+}
+
+static int sis190_alloc_rx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff,
+ struct RxDesc *desc, u32 rx_buf_sz)
+{
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+ int ret = 0;
+
+ skb = dev_alloc_skb(rx_buf_sz);
+ if (!skb)
+ goto err_out;
+
+ *sk_buff = skb;
+
+ mapping = pci_map_single(pdev, skb->data, rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+
+ sis190_map_to_asic(desc, mapping, rx_buf_sz);
+out:
+ return ret;
+
+err_out:
+ ret = -ENOMEM;
+ sis190_make_unusable_by_asic(desc);
+ goto out;
+}
+
+static u32 sis190_rx_fill(struct sis190_private *tp, struct net_device *dev,
+ u32 start, u32 end)
+{
+ u32 cur;
+
+ for (cur = start; cur < end; cur++) {
+ int ret, i = cur % NUM_RX_DESC;
+
+ if (tp->Rx_skbuff[i])
+ continue;
+
+ ret = sis190_alloc_rx_skb(tp->pci_dev, tp->Rx_skbuff + i,
+ tp->RxDescRing + i, tp->rx_buf_sz);
+ if (ret < 0)
+ break;
+ }
+ return cur - start;
+}
+
+static inline int sis190_try_rx_copy(struct sk_buff **sk_buff, int pkt_size,
+ struct RxDesc *desc, int rx_buf_sz)
+{
+ int ret = -1;
+
+ if (pkt_size < rx_copybreak) {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_size + NET_IP_ALIGN);
+ if (skb) {
+ skb_reserve(skb, NET_IP_ALIGN);
+ eth_copy_and_sum(skb, sk_buff[0]->data, pkt_size, 0);
+ *sk_buff = skb;
+ sis190_give_to_asic(desc, rx_buf_sz);
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static inline int sis190_rx_pkt_err(u32 status, struct net_device_stats *stats)
+{
+#define ErrMask (OVRUN | SHORT | LIMIT | MIIER | NIBON | COLON | ABORT)
+
+ if ((status & CRCOK) && !(status & ErrMask))
+ return 0;
+
+ if (!(status & CRCOK))
+ stats->rx_crc_errors++;
+ else if (status & OVRUN)
+ stats->rx_over_errors++;
+ else if (status & (SHORT | LIMIT))
+ stats->rx_length_errors++;
+ else if (status & (MIIER | NIBON | COLON))
+ stats->rx_frame_errors++;
+
+ stats->rx_errors++;
+ return -1;
+}
+
+static int sis190_rx_interrupt(struct net_device *dev,
+ struct sis190_private *tp, void __iomem *ioaddr)
+{
+ struct net_device_stats *stats = &tp->stats;
+ u32 rx_left, cur_rx = tp->cur_rx;
+ u32 delta, count;
+
+ rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx;
+ rx_left = sis190_rx_quota(rx_left, (u32) dev->quota);
+
+ for (; rx_left > 0; rx_left--, cur_rx++) {
+ unsigned int entry = cur_rx % NUM_RX_DESC;
+ struct RxDesc *desc = tp->RxDescRing + entry;
+ u32 status;
+
+ if (desc->status & OWNbit)
+ break;
+
+ status = le32_to_cpu(desc->PSize);
+
+ // net_intr(tp, KERN_INFO "%s: Rx PSize = %08x.\n", dev->name,
+ // status);
+
+ if (sis190_rx_pkt_err(status, stats) < 0)
+ sis190_give_to_asic(desc, tp->rx_buf_sz);
+ else {
+ struct sk_buff *skb = tp->Rx_skbuff[entry];
+ int pkt_size = (status & RxSizeMask) - 4;
+ void (*pci_action)(struct pci_dev *, dma_addr_t,
+ size_t, int) = pci_dma_sync_single_for_device;
+
+ if (unlikely(pkt_size > tp->rx_buf_sz)) {
+ net_intr(tp, KERN_INFO
+ "%s: (frag) status = %08x.\n",
+ dev->name, status);
+ stats->rx_dropped++;
+ stats->rx_length_errors++;
+ sis190_give_to_asic(desc, tp->rx_buf_sz);
+ continue;
+ }
+
+ pci_dma_sync_single_for_cpu(tp->pci_dev,
+ le32_to_cpu(desc->addr), tp->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+
+ if (sis190_try_rx_copy(&skb, pkt_size, desc,
+ tp->rx_buf_sz)) {
+ pci_action = pci_unmap_single;
+ tp->Rx_skbuff[entry] = NULL;
+ sis190_make_unusable_by_asic(desc);
+ }
+
+ pci_action(tp->pci_dev, le32_to_cpu(desc->addr),
+ tp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+
+ skb->dev = dev;
+ skb_put(skb, pkt_size);
+ skb->protocol = eth_type_trans(skb, dev);
+
+ sis190_rx_skb(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += pkt_size;
+ if ((status & BCAST) == MCAST)
+ stats->multicast++;
+ }
+ }
+ count = cur_rx - tp->cur_rx;
+ tp->cur_rx = cur_rx;
+
+ delta = sis190_rx_fill(tp, dev, tp->dirty_rx, tp->cur_rx);
+ if (!delta && count && netif_msg_intr(tp))
+ printk(KERN_INFO "%s: no Rx buffer allocated.\n", dev->name);
+ tp->dirty_rx += delta;
+
+ if (((tp->dirty_rx + NUM_RX_DESC) == tp->cur_rx) && netif_msg_intr(tp))
+ printk(KERN_EMERG "%s: Rx buffers exhausted.\n", dev->name);
+
+ return count;
+}
+
+static void sis190_unmap_tx_skb(struct pci_dev *pdev, struct sk_buff *skb,
+ struct TxDesc *desc)
+{
+ unsigned int len;
+
+ len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
+
+ pci_unmap_single(pdev, le32_to_cpu(desc->addr), len, PCI_DMA_TODEVICE);
+
+ memset(desc, 0x00, sizeof(*desc));
+}
+
+static void sis190_tx_interrupt(struct net_device *dev,
+ struct sis190_private *tp, void __iomem *ioaddr)
+{
+ u32 pending, dirty_tx = tp->dirty_tx;
+ /*
+ * It would not be needed if queueing was allowed to be enabled
+ * again too early (hint: think preempt and unclocked smp systems).
+ */
+ unsigned int queue_stopped;
+
+ smp_rmb();
+ pending = tp->cur_tx - dirty_tx;
+ queue_stopped = (pending == NUM_TX_DESC);
+
+ for (; pending; pending--, dirty_tx++) {
+ unsigned int entry = dirty_tx % NUM_TX_DESC;
+ struct TxDesc *txd = tp->TxDescRing + entry;
+ struct sk_buff *skb;
+
+ if (le32_to_cpu(txd->status) & OWNbit)
+ break;
+
+ skb = tp->Tx_skbuff[entry];
+
+ tp->stats.tx_packets++;
+ tp->stats.tx_bytes += skb->len;
+
+ sis190_unmap_tx_skb(tp->pci_dev, skb, txd);
+ tp->Tx_skbuff[entry] = NULL;
+ dev_kfree_skb_irq(skb);
+ }
+
+ if (tp->dirty_tx != dirty_tx) {
+ tp->dirty_tx = dirty_tx;
+ smp_wmb();
+ if (queue_stopped)
+ netif_wake_queue(dev);
+ }
+}
+
+/*
+ * The interrupt handler does all of the Rx thread work and cleans up after
+ * the Tx thread.
+ */
+static irqreturn_t sis190_interrupt(int irq, void *__dev, struct pt_regs *regs)
+{
+ struct net_device *dev = __dev;
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned int handled = 0;
+ u32 status;
+
+ status = SIS_R32(IntrStatus);
+
+ if ((status == 0xffffffff) || !status)
+ goto out;
+
+ handled = 1;
+
+ if (unlikely(!netif_running(dev))) {
+ sis190_asic_down(ioaddr);
+ goto out;
+ }
+
+ SIS_W32(IntrStatus, status);
+
+ // net_intr(tp, KERN_INFO "%s: status = %08x.\n", dev->name, status);
+
+ if (status & LinkChange) {
+ net_intr(tp, KERN_INFO "%s: link change.\n", dev->name);
+ schedule_work(&tp->phy_task);
+ }
+
+ if (status & RxQInt)
+ sis190_rx_interrupt(dev, tp, ioaddr);
+
+ if (status & TxQ0Int)
+ sis190_tx_interrupt(dev, tp, ioaddr);
+out:
+ return IRQ_RETVAL(handled);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void sis190_netpoll(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ struct pci_dev *pdev = tp->pci_dev;
+
+ disable_irq(pdev->irq);
+ sis190_interrupt(pdev->irq, dev, NULL);
+ enable_irq(pdev->irq);
+}
+#endif
+
+static void sis190_free_rx_skb(struct sis190_private *tp,
+ struct sk_buff **sk_buff, struct RxDesc *desc)
+{
+ struct pci_dev *pdev = tp->pci_dev;
+
+ pci_unmap_single(pdev, le32_to_cpu(desc->addr), tp->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(*sk_buff);
+ *sk_buff = NULL;
+ sis190_make_unusable_by_asic(desc);
+}
+
+static void sis190_rx_clear(struct sis190_private *tp)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ if (!tp->Rx_skbuff[i])
+ continue;
+ sis190_free_rx_skb(tp, tp->Rx_skbuff + i, tp->RxDescRing + i);
+ }
+}
+
+static void sis190_init_ring_indexes(struct sis190_private *tp)
+{
+ tp->dirty_tx = tp->dirty_rx = tp->cur_tx = tp->cur_rx = 0;
+}
+
+static int sis190_init_ring(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ sis190_init_ring_indexes(tp);
+
+ memset(tp->Tx_skbuff, 0x0, NUM_TX_DESC * sizeof(struct sk_buff *));
+ memset(tp->Rx_skbuff, 0x0, NUM_RX_DESC * sizeof(struct sk_buff *));
+
+ if (sis190_rx_fill(tp, dev, 0, NUM_RX_DESC) != NUM_RX_DESC)
+ goto err_rx_clear;
+
+ sis190_mark_as_last_descriptor(tp->RxDescRing + NUM_RX_DESC - 1);
+
+ return 0;
+
+err_rx_clear:
+ sis190_rx_clear(tp);
+ return -ENOMEM;
+}
+
+static void sis190_set_rx_mode(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+ u32 mc_filter[2]; /* Multicast hash filter */
+ u16 rx_mode;
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Unconditionally log net taps. */
+ net_drv(tp, KERN_NOTICE "%s: Promiscuous mode enabled.\n",
+ dev->name);
+ rx_mode =
+ AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
+ AcceptAllPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else if ((dev->mc_count > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else {
+ struct dev_mc_list *mclist;
+ unsigned int i;
+
+ rx_mode = AcceptBroadcast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0;
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next) {
+ int bit_nr =
+ ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
+ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
+ rx_mode |= AcceptMulticast;
+ }
+ }
+
+ spin_lock_irqsave(&tp->lock, flags);
+
+ SIS_W16(RxMacControl, rx_mode | 0x2);
+ SIS_W32(RxHashTable, mc_filter[0]);
+ SIS_W32(RxHashTable + 4, mc_filter[1]);
+
+ spin_unlock_irqrestore(&tp->lock, flags);
+}
+
+static void sis190_soft_reset(void __iomem *ioaddr)
+{
+ SIS_W32(IntrControl, 0x8000);
+ SIS_PCI_COMMIT();
+ msleep(1);
+ SIS_W32(IntrControl, 0x0);
+ sis190_asic_down(ioaddr);
+ msleep(1);
+}
+
+static void sis190_hw_start(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ sis190_soft_reset(ioaddr);
+
+ SIS_W32(TxDescStartAddr, tp->tx_dma);
+ SIS_W32(RxDescStartAddr, tp->rx_dma);
+
+ SIS_W32(IntrStatus, 0xffffffff);
+ SIS_W32(IntrMask, 0x0);
+ /*
+ * Default is 100Mbps.
+ * A bit strange: 100Mbps is 0x1801 elsewhere -- FR 2005/06/09
+ */
+ SIS_W16(StationControl, 0x1901);
+ SIS_W32(GMIIControl, 0x0);
+ SIS_W32(TxMacControl, 0x60);
+ SIS_W16(RxMacControl, 0x02);
+ SIS_W32(RxHashTable, 0x0);
+ SIS_W32(0x6c, 0x0);
+ SIS_W32(RxWolCtrl, 0x0);
+ SIS_W32(RxWolData, 0x0);
+
+ SIS_PCI_COMMIT();
+
+ sis190_set_rx_mode(dev);
+
+ /* Enable all known interrupts by setting the interrupt mask. */
+ SIS_W32(IntrMask, sis190_intr_mask);
+
+ SIS_W32(TxControl, 0x1a00 | CmdTxEnb);
+ SIS_W32(RxControl, 0x1a1d);
+
+ netif_start_queue(dev);
+}
+
+static void sis190_phy_task(void * data)
+{
+ struct net_device *dev = data;
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int phy_id = tp->mii_if.phy_id;
+ u16 val;
+
+ rtnl_lock();
+
+ val = mdio_read(ioaddr, phy_id, MII_BMCR);
+ if (val & BMCR_RESET) {
+ // FIXME: needlessly high ? -- FR 02/07/2005
+ mod_timer(&tp->timer, jiffies + HZ/10);
+ } else if (!(mdio_read_latched(ioaddr, phy_id, MII_BMSR) &
+ BMSR_ANEGCOMPLETE)) {
+ net_link(tp, KERN_WARNING "%s: PHY reset until link up.\n",
+ dev->name);
+ mdio_write(ioaddr, phy_id, MII_BMCR, val | BMCR_RESET);
+ mod_timer(&tp->timer, jiffies + SIS190_PHY_TIMEOUT);
+ } else {
+ /* Rejoice ! */
+ struct {
+ int val;
+ const char *msg;
+ u16 ctl;
+ } reg31[] = {
+ { LPA_1000XFULL | LPA_SLCT,
+ "1000 Mbps Full Duplex",
+ 0x01 | _1000bpsF },
+ { LPA_1000XHALF | LPA_SLCT,
+ "1000 Mbps Half Duplex",
+ 0x01 | _1000bpsH },
+ { LPA_100FULL,
+ "100 Mbps Full Duplex",
+ 0x01 | _100bpsF },
+ { LPA_100HALF,
+ "100 Mbps Half Duplex",
+ 0x01 | _100bpsH },
+ { LPA_10FULL,
+ "10 Mbps Full Duplex",
+ 0x01 | _10bpsF },
+ { LPA_10HALF,
+ "10 Mbps Half Duplex",
+ 0x01 | _10bpsH },
+ { 0, "unknown", 0x0000 }
+ }, *p;
+ u16 adv;
+
+ val = mdio_read(ioaddr, phy_id, 0x1f);
+ net_link(tp, KERN_INFO "%s: mii ext = %04x.\n", dev->name, val);
+
+ val = mdio_read(ioaddr, phy_id, MII_LPA);
+ adv = mdio_read(ioaddr, phy_id, MII_ADVERTISE);
+ net_link(tp, KERN_INFO "%s: mii lpa = %04x adv = %04x.\n",
+ dev->name, val, adv);
+
+ val &= adv;
+
+ for (p = reg31; p->ctl; p++) {
+ if ((val & p->val) == p->val)
+ break;
+ }
+ if (p->ctl)
+ SIS_W16(StationControl, p->ctl);
+ net_link(tp, KERN_INFO "%s: link on %s mode.\n", dev->name,
+ p->msg);
+ netif_carrier_on(dev);
+ }
+
+ rtnl_unlock();
+}
+
+static void sis190_phy_timer(unsigned long __opaque)
+{
+ struct net_device *dev = (struct net_device *)__opaque;
+ struct sis190_private *tp = netdev_priv(dev);
+
+ if (likely(netif_running(dev)))
+ schedule_work(&tp->phy_task);
+}
+
+static inline void sis190_delete_timer(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ del_timer_sync(&tp->timer);
+}
+
+static inline void sis190_request_timer(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ struct timer_list *timer = &tp->timer;
+
+ init_timer(timer);
+ timer->expires = jiffies + SIS190_PHY_TIMEOUT;
+ timer->data = (unsigned long)dev;
+ timer->function = sis190_phy_timer;
+ add_timer(timer);
+}
+
+static void sis190_set_rxbufsize(struct sis190_private *tp,
+ struct net_device *dev)
+{
+ unsigned int mtu = dev->mtu;
+
+ tp->rx_buf_sz = (mtu > RX_BUF_SIZE) ? mtu + ETH_HLEN + 8 : RX_BUF_SIZE;
+ /* RxDesc->size has a licence to kill the lower bits */
+ if (tp->rx_buf_sz & 0x07) {
+ tp->rx_buf_sz += 8;
+ tp->rx_buf_sz &= RX_BUF_MASK;
+ }
+}
+
+static int sis190_open(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ struct pci_dev *pdev = tp->pci_dev;
+ int rc = -ENOMEM;
+
+ sis190_set_rxbufsize(tp, dev);
+
+ /*
+ * Rx and Tx descriptors need 256 bytes alignment.
+ * pci_alloc_consistent() guarantees a stronger alignment.
+ */
+ tp->TxDescRing = pci_alloc_consistent(pdev, TX_RING_BYTES, &tp->tx_dma);
+ if (!tp->TxDescRing)
+ goto out;
+
+ tp->RxDescRing = pci_alloc_consistent(pdev, RX_RING_BYTES, &tp->rx_dma);
+ if (!tp->RxDescRing)
+ goto err_free_tx_0;
+
+ rc = sis190_init_ring(dev);
+ if (rc < 0)
+ goto err_free_rx_1;
+
+ INIT_WORK(&tp->phy_task, sis190_phy_task, dev);
+
+ sis190_request_timer(dev);
+
+ rc = request_irq(dev->irq, sis190_interrupt, SA_SHIRQ, dev->name, dev);
+ if (rc < 0)
+ goto err_release_timer_2;
+
+ sis190_hw_start(dev);
+out:
+ return rc;
+
+err_release_timer_2:
+ sis190_delete_timer(dev);
+ sis190_rx_clear(tp);
+err_free_rx_1:
+ pci_free_consistent(tp->pci_dev, RX_RING_BYTES, tp->RxDescRing,
+ tp->rx_dma);
+err_free_tx_0:
+ pci_free_consistent(tp->pci_dev, TX_RING_BYTES, tp->TxDescRing,
+ tp->tx_dma);
+ goto out;
+}
+
+static void sis190_tx_clear(struct sis190_private *tp)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_TX_DESC; i++) {
+ struct sk_buff *skb = tp->Tx_skbuff[i];
+
+ if (!skb)
+ continue;
+
+ sis190_unmap_tx_skb(tp->pci_dev, skb, tp->TxDescRing + i);
+ tp->Tx_skbuff[i] = NULL;
+ dev_kfree_skb(skb);
+
+ tp->stats.tx_dropped++;
+ }
+ tp->cur_tx = tp->dirty_tx = 0;
+}
+
+static void sis190_down(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned int poll_locked = 0;
+
+ sis190_delete_timer(dev);
+
+ netif_stop_queue(dev);
+
+ flush_scheduled_work();
+
+ do {
+ spin_lock_irq(&tp->lock);
+
+ sis190_asic_down(ioaddr);
+
+ spin_unlock_irq(&tp->lock);
+
+ synchronize_irq(dev->irq);
+
+ if (!poll_locked) {
+ netif_poll_disable(dev);
+ poll_locked++;
+ }
+
+ synchronize_sched();
+
+ } while (SIS_R32(IntrMask));
+
+ sis190_tx_clear(tp);
+ sis190_rx_clear(tp);
+}
+
+static int sis190_close(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ struct pci_dev *pdev = tp->pci_dev;
+
+ sis190_down(dev);
+
+ free_irq(dev->irq, dev);
+
+ netif_poll_enable(dev);
+
+ pci_free_consistent(pdev, TX_RING_BYTES, tp->TxDescRing, tp->tx_dma);
+ pci_free_consistent(pdev, RX_RING_BYTES, tp->RxDescRing, tp->rx_dma);
+
+ tp->TxDescRing = NULL;
+ tp->RxDescRing = NULL;
+
+ return 0;
+}
+
+static int sis190_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u32 len, entry, dirty_tx;
+ struct TxDesc *desc;
+ dma_addr_t mapping;
+
+ if (unlikely(skb->len < ETH_ZLEN)) {
+ skb = skb_padto(skb, ETH_ZLEN);
+ if (!skb) {
+ tp->stats.tx_dropped++;
+ goto out;
+ }
+ len = ETH_ZLEN;
+ } else {
+ len = skb->len;
+ }
+
+ entry = tp->cur_tx % NUM_TX_DESC;
+ desc = tp->TxDescRing + entry;
+
+ if (unlikely(le32_to_cpu(desc->status) & OWNbit)) {
+ netif_stop_queue(dev);
+ net_tx_err(tp, KERN_ERR PFX
+ "%s: BUG! Tx Ring full when queue awake!\n",
+ dev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ mapping = pci_map_single(tp->pci_dev, skb->data, len, PCI_DMA_TODEVICE);
+
+ tp->Tx_skbuff[entry] = skb;
+
+ desc->PSize = cpu_to_le32(len);
+ desc->addr = cpu_to_le32(mapping);
+
+ desc->size = cpu_to_le32(len);
+ if (entry == (NUM_TX_DESC - 1))
+ desc->size |= cpu_to_le32(RingEnd);
+
+ wmb();
+
+ desc->status = cpu_to_le32(OWNbit | INTbit | DEFbit | CRCbit | PADbit);
+
+ tp->cur_tx++;
+
+ smp_wmb();
+
+ SIS_W32(TxControl, 0x1a00 | CmdReset | CmdTxEnb);
+
+ dev->trans_start = jiffies;
+
+ dirty_tx = tp->dirty_tx;
+ if ((tp->cur_tx - NUM_TX_DESC) == dirty_tx) {
+ netif_stop_queue(dev);
+ smp_rmb();
+ if (dirty_tx != tp->dirty_tx)
+ netif_wake_queue(dev);
+ }
+out:
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *sis190_get_stats(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ return &tp->stats;
+}
+
+static void sis190_free_phy(struct list_head *first_phy)
+{
+ struct sis190_phy *cur, *next;
+
+ list_for_each_entry_safe(cur, next, first_phy, list) {
+ kfree(cur);
+ }
+}
+
+/**
+ * sis190_default_phy - Select default PHY for sis190 mac.
+ * @dev: the net device to probe for
+ *
+ * Select first detected PHY with link as default.
+ * If no one is link on, select PHY whose types is HOME as default.
+ * If HOME doesn't exist, select LAN.
+ */
+static u16 sis190_default_phy(struct net_device *dev)
+{
+ struct sis190_phy *phy, *phy_home, *phy_default, *phy_lan;
+ struct sis190_private *tp = netdev_priv(dev);
+ struct mii_if_info *mii_if = &tp->mii_if;
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 status;
+
+ phy_home = phy_default = phy_lan = NULL;
+
+ list_for_each_entry(phy, &tp->first_phy, list) {
+ status = mdio_read_latched(ioaddr, phy->phy_id, MII_BMSR);
+
+ // Link ON & Not select default PHY & not ghost PHY.
+ if ((status & BMSR_LSTATUS) &&
+ !phy_default &&
+ (phy->type != UNKNOWN)) {
+ phy_default = phy;
+ } else {
+ status = mdio_read(ioaddr, phy->phy_id, MII_BMCR);
+ mdio_write(ioaddr, phy->phy_id, MII_BMCR,
+ status | BMCR_ANENABLE | BMCR_ISOLATE);
+ if (phy->type == HOME)
+ phy_home = phy;
+ else if (phy->type == LAN)
+ phy_lan = phy;
+ }
+ }
+
+ if (!phy_default) {
+ if (phy_home)
+ phy_default = phy_home;
+ else if (phy_lan)
+ phy_default = phy_lan;
+ else
+ phy_default = list_entry(&tp->first_phy,
+ struct sis190_phy, list);
+ }
+
+ if (mii_if->phy_id != phy_default->phy_id) {
+ mii_if->phy_id = phy_default->phy_id;
+ net_probe(tp, KERN_INFO
+ "%s: Using transceiver at address %d as default.\n",
+ pci_name(tp->pci_dev), mii_if->phy_id);
+ }
+
+ status = mdio_read(ioaddr, mii_if->phy_id, MII_BMCR);
+ status &= (~BMCR_ISOLATE);
+
+ mdio_write(ioaddr, mii_if->phy_id, MII_BMCR, status);
+ status = mdio_read_latched(ioaddr, mii_if->phy_id, MII_BMSR);
+
+ return status;
+}
+
+static void sis190_init_phy(struct net_device *dev, struct sis190_private *tp,
+ struct sis190_phy *phy, unsigned int phy_id,
+ u16 mii_status)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ struct mii_chip_info *p;
+
+ INIT_LIST_HEAD(&phy->list);
+ phy->status = mii_status;
+ phy->phy_id = phy_id;
+
+ phy->id[0] = mdio_read(ioaddr, phy_id, MII_PHYSID1);
+ phy->id[1] = mdio_read(ioaddr, phy_id, MII_PHYSID2);
+
+ for (p = mii_chip_table; p->type; p++) {
+ if ((p->id[0] == phy->id[0]) &&
+ (p->id[1] == (phy->id[1] & 0xfff0))) {
+ break;
+ }
+ }
+
+ if (p->id[1]) {
+ phy->type = (p->type == MIX) ?
+ ((mii_status & (BMSR_100FULL | BMSR_100HALF)) ?
+ LAN : HOME) : p->type;
+ } else
+ phy->type = UNKNOWN;
+
+ net_probe(tp, KERN_INFO "%s: %s transceiver at address %d.\n",
+ pci_name(tp->pci_dev),
+ (phy->type == UNKNOWN) ? "Unknown PHY" : p->name, phy_id);
+}
+
+/**
+ * sis190_mii_probe - Probe MII PHY for sis190
+ * @dev: the net device to probe for
+ *
+ * Search for total of 32 possible mii phy addresses.
+ * Identify and set current phy if found one,
+ * return error if it failed to found.
+ */
+static int __devinit sis190_mii_probe(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ struct mii_if_info *mii_if = &tp->mii_if;
+ void __iomem *ioaddr = tp->mmio_addr;
+ int phy_id;
+ int rc = 0;
+
+ INIT_LIST_HEAD(&tp->first_phy);
+
+ for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) {
+ struct sis190_phy *phy;
+ u16 status;
+
+ status = mdio_read_latched(ioaddr, phy_id, MII_BMSR);
+
+ // Try next mii if the current one is not accessible.
+ if (status == 0xffff || status == 0x0000)
+ continue;
+
+ phy = kmalloc(sizeof(*phy), GFP_KERNEL);
+ if (!phy) {
+ sis190_free_phy(&tp->first_phy);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ sis190_init_phy(dev, tp, phy, phy_id, status);
+
+ list_add(&tp->first_phy, &phy->list);
+ }
+
+ if (list_empty(&tp->first_phy)) {
+ net_probe(tp, KERN_INFO "%s: No MII transceivers found!\n",
+ pci_name(tp->pci_dev));
+ rc = -EIO;
+ goto out;
+ }
+
+ /* Select default PHY for mac */
+ sis190_default_phy(dev);
+
+ mii_if->dev = dev;
+ mii_if->mdio_read = __mdio_read;
+ mii_if->mdio_write = __mdio_write;
+ mii_if->phy_id_mask = PHY_ID_ANY;
+ mii_if->reg_num_mask = MII_REG_ANY;
+out:
+ return rc;
+}
+
+static void __devexit sis190_mii_remove(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ sis190_free_phy(&tp->first_phy);
+}
+
+static void sis190_release_board(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct sis190_private *tp = netdev_priv(dev);
+
+ iounmap(tp->mmio_addr);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ free_netdev(dev);
+}
+
+static struct net_device * __devinit sis190_init_board(struct pci_dev *pdev)
+{
+ struct sis190_private *tp;
+ struct net_device *dev;
+ void __iomem *ioaddr;
+ int rc;
+
+ dev = alloc_etherdev(sizeof(*tp));
+ if (!dev) {
+ net_drv(&debug, KERN_ERR PFX "unable to alloc new ethernet\n");
+ rc = -ENOMEM;
+ goto err_out_0;
+ }
+
+ SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ tp = netdev_priv(dev);
+ tp->msg_enable = netif_msg_init(debug.msg_enable, SIS190_MSG_DEFAULT);
+
+ rc = pci_enable_device(pdev);
+ if (rc < 0) {
+ net_probe(tp, KERN_ERR "%s: enable failure\n", pci_name(pdev));
+ goto err_free_dev_1;
+ }
+
+ rc = -ENODEV;
+
+ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+ net_probe(tp, KERN_ERR "%s: region #0 is no MMIO resource.\n",
+ pci_name(pdev));
+ goto err_pci_disable_2;
+ }
+ if (pci_resource_len(pdev, 0) < SIS190_REGS_SIZE) {
+ net_probe(tp, KERN_ERR "%s: invalid PCI region size(s).\n",
+ pci_name(pdev));
+ goto err_pci_disable_2;
+ }
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc < 0) {
+ net_probe(tp, KERN_ERR PFX "%s: could not request regions.\n",
+ pci_name(pdev));
+ goto err_pci_disable_2;
+ }
+
+ rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc < 0) {
+ net_probe(tp, KERN_ERR "%s: DMA configuration failed.\n",
+ pci_name(pdev));
+ goto err_free_res_3;
+ }
+
+ pci_set_master(pdev);
+
+ ioaddr = ioremap(pci_resource_start(pdev, 0), SIS190_REGS_SIZE);
+ if (!ioaddr) {
+ net_probe(tp, KERN_ERR "%s: cannot remap MMIO, aborting\n",
+ pci_name(pdev));
+ rc = -EIO;
+ goto err_free_res_3;
+ }
+
+ tp->pci_dev = pdev;
+ tp->mmio_addr = ioaddr;
+
+ sis190_irq_mask_and_ack(ioaddr);
+
+ sis190_soft_reset(ioaddr);
+out:
+ return dev;
+
+err_free_res_3:
+ pci_release_regions(pdev);
+err_pci_disable_2:
+ pci_disable_device(pdev);
+err_free_dev_1:
+ free_netdev(dev);
+err_out_0:
+ dev = ERR_PTR(rc);
+ goto out;
+}
+
+static void sis190_tx_timeout(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u8 tmp8;
+
+ /* Disable Tx, if not already */
+ tmp8 = SIS_R8(TxControl);
+ if (tmp8 & CmdTxEnb)
+ SIS_W8(TxControl, tmp8 & ~CmdTxEnb);
+
+
+ net_tx_err(tp, KERN_INFO "%s: Transmit timeout, status %08x %08x.\n",
+ dev->name, SIS_R32(TxControl), SIS_R32(TxSts));
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ SIS_W32(IntrMask, 0x0000);
+
+ /* Stop a shared interrupt from scavenging while we are. */
+ spin_lock_irq(&tp->lock);
+ sis190_tx_clear(tp);
+ spin_unlock_irq(&tp->lock);
+
+ /* ...and finally, reset everything. */
+ sis190_hw_start(dev);
+
+ netif_wake_queue(dev);
+}
+
+static int __devinit sis190_get_mac_addr_from_eeprom(struct pci_dev *pdev,
+ struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 sig;
+ int i;
+
+ net_probe(tp, KERN_INFO "%s: Read MAC address from EEPROM\n",
+ pci_name(pdev));
+
+ /* Check to see if there is a sane EEPROM */
+ sig = (u16) sis190_read_eeprom(ioaddr, EEPROMSignature);
+
+ if ((sig == 0xffff) || (sig == 0x0000)) {
+ net_probe(tp, KERN_INFO "%s: Error EEPROM read %x.\n",
+ pci_name(pdev), sig);
+ return -EIO;
+ }
+
+ /* Get MAC address from EEPROM */
+ for (i = 0; i < MAC_ADDR_LEN / 2; i++) {
+ __le16 w = sis190_read_eeprom(ioaddr, EEPROMMACAddr + i);
+
+ ((u16 *)dev->dev_addr)[0] = le16_to_cpu(w);
+ }
+
+ return 0;
+}
+
+/**
+ * sis190_get_mac_addr_from_apc - Get MAC address for SiS965 model
+ * @pdev: PCI device
+ * @dev: network device to get address for
+ *
+ * SiS965 model, use APC CMOS RAM to store MAC address.
+ * APC CMOS RAM is accessed through ISA bridge.
+ * MAC address is read into @net_dev->dev_addr.
+ */
+static int __devinit sis190_get_mac_addr_from_apc(struct pci_dev *pdev,
+ struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ struct pci_dev *isa_bridge;
+ u8 reg, tmp8;
+ int i;
+
+ net_probe(tp, KERN_INFO "%s: Read MAC address from APC.\n",
+ pci_name(pdev));
+
+ isa_bridge = pci_get_device(PCI_VENDOR_ID_SI, 0x0965, NULL);
+ if (!isa_bridge) {
+ net_probe(tp, KERN_INFO "%s: Can not find ISA bridge.\n",
+ pci_name(pdev));
+ return -EIO;
+ }
+
+ /* Enable port 78h & 79h to access APC Registers. */
+ pci_read_config_byte(isa_bridge, 0x48, &tmp8);
+ reg = (tmp8 & ~0x02);
+ pci_write_config_byte(isa_bridge, 0x48, reg);
+ udelay(50);
+ pci_read_config_byte(isa_bridge, 0x48, &reg);
+
+ for (i = 0; i < MAC_ADDR_LEN; i++) {
+ outb(0x9 + i, 0x78);
+ dev->dev_addr[i] = inb(0x79);
+ }
+
+ outb(0x12, 0x78);
+ reg = inb(0x79);
+
+ /* Restore the value to ISA Bridge */
+ pci_write_config_byte(isa_bridge, 0x48, tmp8);
+ pci_dev_put(isa_bridge);
+
+ return 0;
+}
+
+/**
+ * sis190_init_rxfilter - Initialize the Rx filter
+ * @dev: network device to initialize
+ *
+ * Set receive filter address to our MAC address
+ * and enable packet filtering.
+ */
+static inline void sis190_init_rxfilter(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 ctl;
+ int i;
+
+ ctl = SIS_R16(RxMacControl);
+ /*
+ * Disable packet filtering before setting filter.
+ * Note: SiS's driver writes 32 bits but RxMacControl is 16 bits
+ * only and followed by RxMacAddr (6 bytes). Strange. -- FR
+ */
+ SIS_W16(RxMacControl, ctl & ~0x0f00);
+
+ for (i = 0; i < MAC_ADDR_LEN; i++)
+ SIS_W8(RxMacAddr + i, dev->dev_addr[i]);
+
+ SIS_W16(RxMacControl, ctl);
+ SIS_PCI_COMMIT();
+}
+
+static int sis190_get_mac_addr(struct pci_dev *pdev, struct net_device *dev)
+{
+ u8 from;
+
+ pci_read_config_byte(pdev, 0x73, &from);
+
+ return (from & 0x00000001) ?
+ sis190_get_mac_addr_from_apc(pdev, dev) :
+ sis190_get_mac_addr_from_eeprom(pdev, dev);
+}
+
+static void sis190_set_speed_auto(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int phy_id = tp->mii_if.phy_id;
+ int val;
+
+ net_link(tp, KERN_INFO "%s: Enabling Auto-negotiation.\n", dev->name);
+
+ val = mdio_read(ioaddr, phy_id, MII_ADVERTISE);
+
+ // Enable 10/100 Full/Half Mode, leave MII_ADVERTISE bit4:0
+ // unchanged.
+ mdio_write(ioaddr, phy_id, MII_ADVERTISE, (val & ADVERTISE_SLCT) |
+ ADVERTISE_100FULL | ADVERTISE_10FULL |
+ ADVERTISE_100HALF | ADVERTISE_10HALF);
+
+ // Enable 1000 Full Mode.
+ mdio_write(ioaddr, phy_id, MII_CTRL1000, ADVERTISE_1000FULL);
+
+ // Enable auto-negotiation and restart auto-negotiation.
+ mdio_write(ioaddr, phy_id, MII_BMCR,
+ BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET);
+}
+
+static int sis190_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ return mii_ethtool_gset(&tp->mii_if, cmd);
+}
+
+static int sis190_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ return mii_ethtool_sset(&tp->mii_if, cmd);
+}
+
+static void sis190_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, pci_name(tp->pci_dev));
+}
+
+static int sis190_get_regs_len(struct net_device *dev)
+{
+ return SIS190_REGS_SIZE;
+}
+
+static void sis190_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *p)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ unsigned long flags;
+
+ if (regs->len > SIS190_REGS_SIZE)
+ regs->len = SIS190_REGS_SIZE;
+
+ spin_lock_irqsave(&tp->lock, flags);
+ memcpy_fromio(p, tp->mmio_addr, regs->len);
+ spin_unlock_irqrestore(&tp->lock, flags);
+}
+
+static int sis190_nway_reset(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ return mii_nway_restart(&tp->mii_if);
+}
+
+static u32 sis190_get_msglevel(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ return tp->msg_enable;
+}
+
+static void sis190_set_msglevel(struct net_device *dev, u32 value)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ tp->msg_enable = value;
+}
+
+static struct ethtool_ops sis190_ethtool_ops = {
+ .get_settings = sis190_get_settings,
+ .set_settings = sis190_set_settings,
+ .get_drvinfo = sis190_get_drvinfo,
+ .get_regs_len = sis190_get_regs_len,
+ .get_regs = sis190_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_msglevel = sis190_get_msglevel,
+ .set_msglevel = sis190_set_msglevel,
+ .nway_reset = sis190_nway_reset,
+};
+
+static int sis190_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+
+ return !netif_running(dev) ? -EINVAL :
+ generic_mii_ioctl(&tp->mii_if, if_mii(ifr), cmd, NULL);
+}
+
+static int __devinit sis190_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ static int printed_version = 0;
+ struct sis190_private *tp;
+ struct net_device *dev;
+ void __iomem *ioaddr;
+ int rc;
+
+ if (!printed_version) {
+ net_drv(&debug, KERN_INFO SIS190_DRIVER_NAME " loaded.\n");
+ printed_version = 1;
+ }
+
+ dev = sis190_init_board(pdev);
+ if (IS_ERR(dev)) {
+ rc = PTR_ERR(dev);
+ goto out;
+ }
+
+ tp = netdev_priv(dev);
+ ioaddr = tp->mmio_addr;
+
+ rc = sis190_get_mac_addr(pdev, dev);
+ if (rc < 0)
+ goto err_release_board;
+
+ sis190_init_rxfilter(dev);
+
+ INIT_WORK(&tp->phy_task, sis190_phy_task, dev);
+
+ dev->open = sis190_open;
+ dev->stop = sis190_close;
+ dev->do_ioctl = sis190_ioctl;
+ dev->get_stats = sis190_get_stats;
+ dev->tx_timeout = sis190_tx_timeout;
+ dev->watchdog_timeo = SIS190_TX_TIMEOUT;
+ dev->hard_start_xmit = sis190_start_xmit;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = sis190_netpoll;
+#endif
+ dev->set_multicast_list = sis190_set_rx_mode;
+ SET_ETHTOOL_OPS(dev, &sis190_ethtool_ops);
+ dev->irq = pdev->irq;
+ dev->base_addr = (unsigned long) 0xdead;
+
+ spin_lock_init(&tp->lock);
+
+ rc = sis190_mii_probe(dev);
+ if (rc < 0)
+ goto err_release_board;
+
+ rc = register_netdev(dev);
+ if (rc < 0)
+ goto err_remove_mii;
+
+ pci_set_drvdata(pdev, dev);
+
+ net_probe(tp, KERN_INFO "%s: %s at %p (IRQ: %d), "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+ pci_name(pdev), sis_chip_info[ent->driver_data].name,
+ ioaddr, dev->irq,
+ dev->dev_addr[0], dev->dev_addr[1],
+ dev->dev_addr[2], dev->dev_addr[3],
+ dev->dev_addr[4], dev->dev_addr[5]);
+
+ netif_carrier_off(dev);
+
+ sis190_set_speed_auto(dev);
+out:
+ return rc;
+
+err_remove_mii:
+ sis190_mii_remove(dev);
+err_release_board:
+ sis190_release_board(pdev);
+ goto out;
+}
+
+static void __devexit sis190_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ sis190_mii_remove(dev);
+ unregister_netdev(dev);
+ sis190_release_board(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver sis190_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = sis190_pci_tbl,
+ .probe = sis190_init_one,
+ .remove = __devexit_p(sis190_remove_one),
+};
+
+static int __init sis190_init_module(void)
+{
+ return pci_module_init(&sis190_pci_driver);
+}
+
+static void __exit sis190_cleanup_module(void)
+{
+ pci_unregister_driver(&sis190_pci_driver);
+}
+
+module_init(sis190_init_module);
+module_exit(sis190_cleanup_module);
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index 6d4ab1e333b5..af8263a1580e 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -340,41 +340,92 @@ static struct {
static void tg3_write_indirect_reg32(struct tg3 *tp, u32 off, u32 val)
{
- if ((tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) != 0) {
- spin_lock_bh(&tp->indirect_lock);
- pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off);
- pci_write_config_dword(tp->pdev, TG3PCI_REG_DATA, val);
- spin_unlock_bh(&tp->indirect_lock);
- } else {
- writel(val, tp->regs + off);
- if ((tp->tg3_flags & TG3_FLAG_5701_REG_WRITE_BUG) != 0)
- readl(tp->regs + off);
+ unsigned long flags;
+
+ spin_lock_irqsave(&tp->indirect_lock, flags);
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off);
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_DATA, val);
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
+}
+
+static void tg3_write_flush_reg32(struct tg3 *tp, u32 off, u32 val)
+{
+ writel(val, tp->regs + off);
+ readl(tp->regs + off);
+}
+
+static u32 tg3_read_indirect_reg32(struct tg3 *tp, u32 off)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&tp->indirect_lock, flags);
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off);
+ pci_read_config_dword(tp->pdev, TG3PCI_REG_DATA, &val);
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
+ return val;
+}
+
+static void tg3_write_indirect_mbox(struct tg3 *tp, u32 off, u32 val)
+{
+ unsigned long flags;
+
+ if (off == (MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW)) {
+ pci_write_config_dword(tp->pdev, TG3PCI_RCV_RET_RING_CON_IDX +
+ TG3_64BIT_REG_LOW, val);
+ return;
+ }
+ if (off == (MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW)) {
+ pci_write_config_dword(tp->pdev, TG3PCI_STD_RING_PROD_IDX +
+ TG3_64BIT_REG_LOW, val);
+ return;
+ }
+
+ spin_lock_irqsave(&tp->indirect_lock, flags);
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off + 0x5600);
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_DATA, val);
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
+
+ /* In indirect mode when disabling interrupts, we also need
+ * to clear the interrupt bit in the GRC local ctrl register.
+ */
+ if ((off == (MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW)) &&
+ (val == 0x1)) {
+ pci_write_config_dword(tp->pdev, TG3PCI_MISC_LOCAL_CTRL,
+ tp->grc_local_ctrl|GRC_LCLCTRL_CLEARINT);
}
}
+static u32 tg3_read_indirect_mbox(struct tg3 *tp, u32 off)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&tp->indirect_lock, flags);
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off + 0x5600);
+ pci_read_config_dword(tp->pdev, TG3PCI_REG_DATA, &val);
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
+ return val;
+}
+
static void _tw32_flush(struct tg3 *tp, u32 off, u32 val)
{
- if ((tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) != 0) {
- spin_lock_bh(&tp->indirect_lock);
- pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off);
- pci_write_config_dword(tp->pdev, TG3PCI_REG_DATA, val);
- spin_unlock_bh(&tp->indirect_lock);
- } else {
- void __iomem *dest = tp->regs + off;
- writel(val, dest);
- readl(dest); /* always flush PCI write */
- }
+ tp->write32(tp, off, val);
+ if (!(tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) &&
+ !(tp->tg3_flags & TG3_FLAG_5701_REG_WRITE_BUG) &&
+ !(tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND))
+ tp->read32(tp, off); /* flush */
}
-static inline void _tw32_rx_mbox(struct tg3 *tp, u32 off, u32 val)
+static inline void tw32_mailbox_flush(struct tg3 *tp, u32 off, u32 val)
{
- void __iomem *mbox = tp->regs + off;
- writel(val, mbox);
- if (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER)
- readl(mbox);
+ tp->write32_mbox(tp, off, val);
+ if (!(tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) &&
+ !(tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND))
+ tp->read32_mbox(tp, off);
}
-static inline void _tw32_tx_mbox(struct tg3 *tp, u32 off, u32 val)
+static void tg3_write32_tx_mbox(struct tg3 *tp, u32 off, u32 val)
{
void __iomem *mbox = tp->regs + off;
writel(val, mbox);
@@ -384,46 +435,57 @@ static inline void _tw32_tx_mbox(struct tg3 *tp, u32 off, u32 val)
readl(mbox);
}
-#define tw32_mailbox(reg, val) writel(((val) & 0xffffffff), tp->regs + (reg))
-#define tw32_rx_mbox(reg, val) _tw32_rx_mbox(tp, reg, val)
-#define tw32_tx_mbox(reg, val) _tw32_tx_mbox(tp, reg, val)
+static void tg3_write32(struct tg3 *tp, u32 off, u32 val)
+{
+ writel(val, tp->regs + off);
+}
+
+static u32 tg3_read32(struct tg3 *tp, u32 off)
+{
+ return (readl(tp->regs + off));
+}
+
+#define tw32_mailbox(reg, val) tp->write32_mbox(tp, reg, val)
+#define tw32_mailbox_f(reg, val) tw32_mailbox_flush(tp, (reg), (val))
+#define tw32_rx_mbox(reg, val) tp->write32_rx_mbox(tp, reg, val)
+#define tw32_tx_mbox(reg, val) tp->write32_tx_mbox(tp, reg, val)
+#define tr32_mailbox(reg) tp->read32_mbox(tp, reg)
-#define tw32(reg,val) tg3_write_indirect_reg32(tp,(reg),(val))
+#define tw32(reg,val) tp->write32(tp, reg, val)
#define tw32_f(reg,val) _tw32_flush(tp,(reg),(val))
-#define tw16(reg,val) writew(((val) & 0xffff), tp->regs + (reg))
-#define tw8(reg,val) writeb(((val) & 0xff), tp->regs + (reg))
-#define tr32(reg) readl(tp->regs + (reg))
-#define tr16(reg) readw(tp->regs + (reg))
-#define tr8(reg) readb(tp->regs + (reg))
+#define tr32(reg) tp->read32(tp, reg)
static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val)
{
- spin_lock_bh(&tp->indirect_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&tp->indirect_lock, flags);
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
/* Always leave this as zero. */
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
- spin_unlock_bh(&tp->indirect_lock);
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
}
static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val)
{
- spin_lock_bh(&tp->indirect_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&tp->indirect_lock, flags);
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
pci_read_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
/* Always leave this as zero. */
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
- spin_unlock_bh(&tp->indirect_lock);
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
}
static void tg3_disable_ints(struct tg3 *tp)
{
tw32(TG3PCI_MISC_HOST_CTRL,
(tp->misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT));
- tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001);
- tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
+ tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001);
}
static inline void tg3_cond_int(struct tg3 *tp)
@@ -439,9 +501,8 @@ static void tg3_enable_ints(struct tg3 *tp)
tw32(TG3PCI_MISC_HOST_CTRL,
(tp->misc_host_ctrl & ~MISC_HOST_CTRL_MASK_PCI_INT));
- tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
- (tp->last_tag << 24));
- tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
+ tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
+ (tp->last_tag << 24));
tg3_cond_int(tp);
}
@@ -472,8 +533,6 @@ static inline unsigned int tg3_has_work(struct tg3 *tp)
*/
static void tg3_restart_ints(struct tg3 *tp)
{
- tw32(TG3PCI_MISC_HOST_CTRL,
- (tp->misc_host_ctrl & ~MISC_HOST_CTRL_MASK_PCI_INT));
tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
tp->last_tag << 24);
mmiowb();
@@ -3278,9 +3337,8 @@ static irqreturn_t tg3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* No work, shared interrupt perhaps? re-enable
* interrupts, and flush that PCI write
*/
- tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
+ tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
0x00000000);
- tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
}
} else { /* shared interrupt */
handled = 0;
@@ -3323,9 +3381,8 @@ static irqreturn_t tg3_interrupt_tagged(int irq, void *dev_id, struct pt_regs *r
/* no work, shared interrupt perhaps? re-enable
* interrupts, and flush that PCI write
*/
- tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
- tp->last_tag << 24);
- tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
+ tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
+ tp->last_tag << 24);
}
} else { /* shared interrupt */
handled = 0;
@@ -4216,7 +4273,7 @@ static void tg3_stop_fw(struct tg3 *);
static int tg3_chip_reset(struct tg3 *tp)
{
u32 val;
- u32 flags_save;
+ void (*write_op)(struct tg3 *, u32, u32);
int i;
if (!(tp->tg3_flags2 & TG3_FLG2_SUN_570X))
@@ -4228,8 +4285,9 @@ static int tg3_chip_reset(struct tg3 *tp)
* fun things. So, temporarily disable the 5701
* hardware workaround, while we do the reset.
*/
- flags_save = tp->tg3_flags;
- tp->tg3_flags &= ~TG3_FLAG_5701_REG_WRITE_BUG;
+ write_op = tp->write32;
+ if (write_op == tg3_write_flush_reg32)
+ tp->write32 = tg3_write32;
/* do the reset */
val = GRC_MISC_CFG_CORECLK_RESET;
@@ -4248,8 +4306,8 @@ static int tg3_chip_reset(struct tg3 *tp)
val |= GRC_MISC_CFG_KEEP_GPHY_POWER;
tw32(GRC_MISC_CFG, val);
- /* restore 5701 hardware bug workaround flag */
- tp->tg3_flags = flags_save;
+ /* restore 5701 hardware bug workaround write method */
+ tp->write32 = write_op;
/* Unfortunately, we have to delay before the PCI read back.
* Some 575X chips even will not respond to a PCI cfg access
@@ -4635,7 +4693,6 @@ static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base, u32 cpu_scratch_b
int cpu_scratch_size, struct fw_info *info)
{
int err, i;
- u32 orig_tg3_flags = tp->tg3_flags;
void (*write_op)(struct tg3 *, u32, u32);
if (cpu_base == TX_CPU_BASE &&
@@ -4651,11 +4708,6 @@ static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base, u32 cpu_scratch_b
else
write_op = tg3_write_indirect_reg32;
- /* Force use of PCI config space for indirect register
- * write calls.
- */
- tp->tg3_flags |= TG3_FLAG_PCIX_TARGET_HWBUG;
-
/* It is possible that bootcode is still loading at this point.
* Get the nvram lock first before halting the cpu.
*/
@@ -4691,7 +4743,6 @@ static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base, u32 cpu_scratch_b
err = 0;
out:
- tp->tg3_flags = orig_tg3_flags;
return err;
}
@@ -5808,8 +5859,7 @@ static int tg3_reset_hw(struct tg3 *tp)
tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl);
udelay(100);
- tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0);
- tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
+ tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0);
tp->last_tag = 0;
if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) {
@@ -6198,7 +6248,8 @@ static int tg3_test_interrupt(struct tg3 *tp)
HOSTCC_MODE_NOW);
for (i = 0; i < 5; i++) {
- int_mbox = tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
+ int_mbox = tr32_mailbox(MAILBOX_INTERRUPT_0 +
+ TG3_64BIT_REG_LOW);
if (int_mbox != 0)
break;
msleep(10);
@@ -6598,10 +6649,10 @@ static int tg3_open(struct net_device *dev)
/* Mailboxes */
printk("DEBUG: SNDHOST_PROD[%08x%08x] SNDNIC_PROD[%08x%08x]\n",
- tr32(MAILBOX_SNDHOST_PROD_IDX_0 + 0x0),
- tr32(MAILBOX_SNDHOST_PROD_IDX_0 + 0x4),
- tr32(MAILBOX_SNDNIC_PROD_IDX_0 + 0x0),
- tr32(MAILBOX_SNDNIC_PROD_IDX_0 + 0x4));
+ tr32_mailbox(MAILBOX_SNDHOST_PROD_IDX_0 + 0x0),
+ tr32_mailbox(MAILBOX_SNDHOST_PROD_IDX_0 + 0x4),
+ tr32_mailbox(MAILBOX_SNDNIC_PROD_IDX_0 + 0x0),
+ tr32_mailbox(MAILBOX_SNDNIC_PROD_IDX_0 + 0x4));
/* NIC side send descriptors. */
for (i = 0; i < 6; i++) {
@@ -7901,7 +7952,7 @@ static int tg3_test_loopback(struct tg3 *tp)
num_pkts++;
tw32_tx_mbox(MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW, send_idx);
- tr32(MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW);
+ tr32_mailbox(MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW);
udelay(10);
@@ -9153,14 +9204,6 @@ static int __devinit tg3_is_sun_570X(struct tg3 *tp)
static int __devinit tg3_get_invariants(struct tg3 *tp)
{
static struct pci_device_id write_reorder_chipsets[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_82801AA_8) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_82801AB_8) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_82801BA_11) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_82801BA_6) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD,
PCI_DEVICE_ID_AMD_FE_GATE_700C) },
{ },
@@ -9177,7 +9220,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tp->tg3_flags2 |= TG3_FLG2_SUN_570X;
#endif
- /* If we have an AMD 762 or Intel ICH/ICH0/ICH2 chipset, write
+ /* If we have an AMD 762 chipset, write
* reordering to the mailbox registers done by the host
* controller can cause major troubles. We read back from
* every mailbox register write to force the writes to be
@@ -9215,6 +9258,69 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
if (tp->pci_chip_rev_id == CHIPREV_ID_5752_A0_HW)
tp->pci_chip_rev_id = CHIPREV_ID_5752_A0;
+ /* If we have 5702/03 A1 or A2 on certain ICH chipsets,
+ * we need to disable memory and use config. cycles
+ * only to access all registers. The 5702/03 chips
+ * can mistakenly decode the special cycles from the
+ * ICH chipsets as memory write cycles, causing corruption
+ * of register and memory space. Only certain ICH bridges
+ * will drive special cycles with non-zero data during the
+ * address phase which can fall within the 5703's address
+ * range. This is not an ICH bug as the PCI spec allows
+ * non-zero address during special cycles. However, only
+ * these ICH bridges are known to drive non-zero addresses
+ * during special cycles.
+ *
+ * Since special cycles do not cross PCI bridges, we only
+ * enable this workaround if the 5703 is on the secondary
+ * bus of these ICH bridges.
+ */
+ if ((tp->pci_chip_rev_id == CHIPREV_ID_5703_A1) ||
+ (tp->pci_chip_rev_id == CHIPREV_ID_5703_A2)) {
+ static struct tg3_dev_id {
+ u32 vendor;
+ u32 device;
+ u32 rev;
+ } ich_chipsets[] = {
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_8,
+ PCI_ANY_ID },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_8,
+ PCI_ANY_ID },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_11,
+ 0xa },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_6,
+ PCI_ANY_ID },
+ { },
+ };
+ struct tg3_dev_id *pci_id = &ich_chipsets[0];
+ struct pci_dev *bridge = NULL;
+
+ while (pci_id->vendor != 0) {
+ bridge = pci_get_device(pci_id->vendor, pci_id->device,
+ bridge);
+ if (!bridge) {
+ pci_id++;
+ continue;
+ }
+ if (pci_id->rev != PCI_ANY_ID) {
+ u8 rev;
+
+ pci_read_config_byte(bridge, PCI_REVISION_ID,
+ &rev);
+ if (rev > pci_id->rev)
+ continue;
+ }
+ if (bridge->subordinate &&
+ (bridge->subordinate->number ==
+ tp->pdev->bus->number)) {
+
+ tp->tg3_flags2 |= TG3_FLG2_ICH_WORKAROUND;
+ pci_dev_put(bridge);
+ break;
+ }
+ }
+ }
+
/* Find msi capability. */
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780)
tp->msi_cap = pci_find_capability(tp->pdev, PCI_CAP_ID_MSI);
@@ -9302,6 +9408,12 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
}
}
+ /* 5700 BX chips need to have their TX producer index mailboxes
+ * written twice to workaround a bug.
+ */
+ if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX)
+ tp->tg3_flags |= TG3_FLAG_TXD_MBOX_HWBUG;
+
/* Back to back register writes can cause problems on this chip,
* the workaround is to read back all reg writes except those to
* mailbox regs. See tg3_write_indirect_reg32().
@@ -9325,6 +9437,43 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE, pci_state_reg);
}
+ /* Default fast path register access methods */
+ tp->read32 = tg3_read32;
+ tp->write32 = tg3_write32;
+ tp->read32_mbox = tg3_read32;
+ tp->write32_mbox = tg3_write32;
+ tp->write32_tx_mbox = tg3_write32;
+ tp->write32_rx_mbox = tg3_write32;
+
+ /* Various workaround register access methods */
+ if (tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG)
+ tp->write32 = tg3_write_indirect_reg32;
+ else if (tp->tg3_flags & TG3_FLAG_5701_REG_WRITE_BUG)
+ tp->write32 = tg3_write_flush_reg32;
+
+ if ((tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG) ||
+ (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER)) {
+ tp->write32_tx_mbox = tg3_write32_tx_mbox;
+ if (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER)
+ tp->write32_rx_mbox = tg3_write_flush_reg32;
+ }
+
+ if (tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND) {
+ tp->read32 = tg3_read_indirect_reg32;
+ tp->write32 = tg3_write_indirect_reg32;
+ tp->read32_mbox = tg3_read_indirect_mbox;
+ tp->write32_mbox = tg3_write_indirect_mbox;
+ tp->write32_tx_mbox = tg3_write_indirect_mbox;
+ tp->write32_rx_mbox = tg3_write_indirect_mbox;
+
+ iounmap(tp->regs);
+ tp->regs = 0;
+
+ pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd);
+ pci_cmd &= ~PCI_COMMAND_MEMORY;
+ pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd);
+ }
+
/* Get eeprom hw config before calling tg3_set_power_state().
* In particular, the TG3_FLAG_EEPROM_WRITE_PROT flag must be
* determined before calling tg3_set_power_state() so that
@@ -9539,14 +9688,6 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
else
tp->tg3_flags &= ~TG3_FLAG_POLL_SERDES;
- /* 5700 BX chips need to have their TX producer index mailboxes
- * written twice to workaround a bug.
- */
- if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX)
- tp->tg3_flags |= TG3_FLAG_TXD_MBOX_HWBUG;
- else
- tp->tg3_flags &= ~TG3_FLAG_TXD_MBOX_HWBUG;
-
/* It seems all chips can get confused if TX buffers
* straddle the 4GB address boundary in some cases.
*/
@@ -10469,7 +10610,10 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
return 0;
err_out_iounmap:
- iounmap(tp->regs);
+ if (tp->regs) {
+ iounmap(tp->regs);
+ tp->regs = 0;
+ }
err_out_free_dev:
free_netdev(dev);
@@ -10491,7 +10635,10 @@ static void __devexit tg3_remove_one(struct pci_dev *pdev)
struct tg3 *tp = netdev_priv(dev);
unregister_netdev(dev);
- iounmap(tp->regs);
+ if (tp->regs) {
+ iounmap(tp->regs);
+ tp->regs = 0;
+ }
free_netdev(dev);
pci_release_regions(pdev);
pci_disable_device(pdev);
diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h
index 5c4433c147fa..c184b773e585 100644
--- a/drivers/net/tg3.h
+++ b/drivers/net/tg3.h
@@ -2049,6 +2049,11 @@ struct tg3 {
spinlock_t lock;
spinlock_t indirect_lock;
+ u32 (*read32) (struct tg3 *, u32);
+ void (*write32) (struct tg3 *, u32, u32);
+ u32 (*read32_mbox) (struct tg3 *, u32);
+ void (*write32_mbox) (struct tg3 *, u32,
+ u32);
void __iomem *regs;
struct net_device *dev;
struct pci_dev *pdev;
@@ -2060,6 +2065,8 @@ struct tg3 {
u32 msg_enable;
/* begin "tx thread" cacheline section */
+ void (*write32_tx_mbox) (struct tg3 *, u32,
+ u32);
u32 tx_prod;
u32 tx_cons;
u32 tx_pending;
@@ -2071,6 +2078,8 @@ struct tg3 {
dma_addr_t tx_desc_mapping;
/* begin "rx thread" cacheline section */
+ void (*write32_rx_mbox) (struct tg3 *, u32,
+ u32);
u32 rx_rcb_ptr;
u32 rx_std_ptr;
u32 rx_jumbo_ptr;
@@ -2165,6 +2174,7 @@ struct tg3 {
#define TG3_FLG2_ANY_SERDES (TG3_FLG2_PHY_SERDES | \
TG3_FLG2_MII_SERDES)
#define TG3_FLG2_PARALLEL_DETECT 0x01000000
+#define TG3_FLG2_ICH_WORKAROUND 0x02000000
u32 split_mode_max_reqs;
#define SPLIT_MODE_5704_MAX_REQ 3
diff --git a/drivers/net/tulip/Kconfig b/drivers/net/tulip/Kconfig
index e2cdaf876201..8c9634a98c11 100644
--- a/drivers/net/tulip/Kconfig
+++ b/drivers/net/tulip/Kconfig
@@ -135,6 +135,18 @@ config DM9102
<file:Documentation/networking/net-modules.txt>. The module will
be called dmfe.
+config ULI526X
+ tristate "ULi M526x controller support"
+ depends on NET_TULIP && PCI
+ select CRC32
+ ---help---
+ This driver is for ULi M5261/M5263 10/100M Ethernet Controller
+ (<http://www.uli.com.tw/>).
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module will
+ be called uli526x.
+
config PCMCIA_XIRCOM
tristate "Xircom CardBus support (new driver)"
depends on NET_TULIP && CARDBUS
diff --git a/drivers/net/tulip/Makefile b/drivers/net/tulip/Makefile
index 8bb9b4683979..451090d6fcca 100644
--- a/drivers/net/tulip/Makefile
+++ b/drivers/net/tulip/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_WINBOND_840) += winbond-840.o
obj-$(CONFIG_DE2104X) += de2104x.o
obj-$(CONFIG_TULIP) += tulip.o
obj-$(CONFIG_DE4X5) += de4x5.o
+obj-$(CONFIG_ULI526X) += uli526x.o
# Declare multi-part drivers.
diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c
index fc353e348f9a..a22d00198e4d 100644
--- a/drivers/net/tulip/de2104x.c
+++ b/drivers/net/tulip/de2104x.c
@@ -1934,7 +1934,7 @@ static int __init de_init_one (struct pci_dev *pdev,
struct de_private *de;
int rc;
void __iomem *regs;
- long pciaddr;
+ unsigned long pciaddr;
static int board_idx = -1;
board_idx++;
diff --git a/drivers/net/tulip/media.c b/drivers/net/tulip/media.c
index e26c31f944bf..f53396fe79c9 100644
--- a/drivers/net/tulip/media.c
+++ b/drivers/net/tulip/media.c
@@ -81,25 +81,6 @@ int tulip_mdio_read(struct net_device *dev, int phy_id, int location)
return retval & 0xffff;
}
- if(tp->chip_id == ULI526X && tp->revision >= 0x40) {
- int value;
- int i = 1000;
-
- value = ioread32(ioaddr + CSR9);
- iowrite32(value & 0xFFEFFFFF, ioaddr + CSR9);
-
- value = (phy_id << 21) | (location << 16) | 0x08000000;
- iowrite32(value, ioaddr + CSR10);
-
- while(--i > 0) {
- mdio_delay();
- if(ioread32(ioaddr + CSR10) & 0x10000000)
- break;
- }
- retval = ioread32(ioaddr + CSR10);
- spin_unlock_irqrestore(&tp->mii_lock, flags);
- return retval & 0xFFFF;
- }
/* Establish sync by sending at least 32 logic ones. */
for (i = 32; i >= 0; i--) {
iowrite32(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
@@ -159,23 +140,6 @@ void tulip_mdio_write(struct net_device *dev, int phy_id, int location, int val)
spin_unlock_irqrestore(&tp->mii_lock, flags);
return;
}
- if (tp->chip_id == ULI526X && tp->revision >= 0x40) {
- int value;
- int i = 1000;
-
- value = ioread32(ioaddr + CSR9);
- iowrite32(value & 0xFFEFFFFF, ioaddr + CSR9);
-
- value = (phy_id << 21) | (location << 16) | 0x04000000 | (val & 0xFFFF);
- iowrite32(value, ioaddr + CSR10);
-
- while(--i > 0) {
- if (ioread32(ioaddr + CSR10) & 0x10000000)
- break;
- }
- spin_unlock_irqrestore(&tp->mii_lock, flags);
- return;
- }
/* Establish sync by sending 32 logic ones. */
for (i = 32; i >= 0; i--) {
diff --git a/drivers/net/tulip/timer.c b/drivers/net/tulip/timer.c
index 691568283553..e058a9fbfe88 100644
--- a/drivers/net/tulip/timer.c
+++ b/drivers/net/tulip/timer.c
@@ -39,7 +39,6 @@ void tulip_timer(unsigned long data)
case MX98713:
case COMPEX9881:
case DM910X:
- case ULI526X:
default: {
struct medialeaf *mleaf;
unsigned char *p;
diff --git a/drivers/net/tulip/tulip.h b/drivers/net/tulip/tulip.h
index 20346d847d9e..05d2d96f7be2 100644
--- a/drivers/net/tulip/tulip.h
+++ b/drivers/net/tulip/tulip.h
@@ -88,7 +88,6 @@ enum chips {
I21145,
DM910X,
CONEXANT,
- ULI526X
};
@@ -482,11 +481,8 @@ static inline void tulip_stop_rxtx(struct tulip_private *tp)
static inline void tulip_restart_rxtx(struct tulip_private *tp)
{
- if(!(tp->chip_id == ULI526X &&
- (tp->revision == 0x40 || tp->revision == 0x50))) {
- tulip_stop_rxtx(tp);
- udelay(5);
- }
+ tulip_stop_rxtx(tp);
+ udelay(5);
tulip_start_rxtx(tp);
}
diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c
index d45d8f56e5b4..6266a9a7e6e3 100644
--- a/drivers/net/tulip/tulip_core.c
+++ b/drivers/net/tulip/tulip_core.c
@@ -199,9 +199,6 @@ struct tulip_chip_table tulip_tbl[] = {
{ "Conexant LANfinity", 256, 0x0001ebef,
HAS_MII | HAS_ACPI, tulip_timer },
- /* ULi526X */
- { "ULi M5261/M5263", 128, 0x0001ebef,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | HAS_ACPI, tulip_timer },
};
@@ -239,10 +236,9 @@ static struct pci_device_id tulip_pci_tbl[] = {
{ 0x1737, 0xAB09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
{ 0x1737, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
{ 0x17B3, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
- { 0x10b9, 0x5261, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ULI526X }, /* ALi 1563 integrated ethernet */
- { 0x10b9, 0x5263, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ULI526X }, /* ALi 1563 integrated ethernet */
{ 0x10b7, 0x9300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* 3Com 3CSOHO100B-TX */
{ 0x14ea, 0xab08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* Planex FNW-3602-TX */
+ { 0x1414, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
{ } /* terminate list */
};
MODULE_DEVICE_TABLE(pci, tulip_pci_tbl);
@@ -522,7 +518,7 @@ static void tulip_tx_timeout(struct net_device *dev)
dev->name);
} else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
|| tp->chip_id == MX98713 || tp->chip_id == COMPEX9881
- || tp->chip_id == DM910X || tp->chip_id == ULI526X) {
+ || tp->chip_id == DM910X) {
printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
"SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
dev->name, ioread32(ioaddr + CSR5), ioread32(ioaddr + CSR12),
@@ -1103,18 +1099,16 @@ static void set_rx_mode(struct net_device *dev)
entry = tp->cur_tx++ % TX_RING_SIZE;
if (entry != 0) {
- /* Avoid a chip errata by prefixing a dummy entry. Don't do
- this on the ULI526X as it triggers a different problem */
- if (!(tp->chip_id == ULI526X && (tp->revision == 0x40 || tp->revision == 0x50))) {
- tp->tx_buffers[entry].skb = NULL;
- tp->tx_buffers[entry].mapping = 0;
- tp->tx_ring[entry].length =
- (entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP) : 0;
- tp->tx_ring[entry].buffer1 = 0;
- /* Must set DescOwned later to avoid race with chip */
- dummy = entry;
- entry = tp->cur_tx++ % TX_RING_SIZE;
- }
+ /* Avoid a chip errata by prefixing a dummy entry. */
+ tp->tx_buffers[entry].skb = NULL;
+ tp->tx_buffers[entry].mapping = 0;
+ tp->tx_ring[entry].length =
+ (entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP) : 0;
+ tp->tx_ring[entry].buffer1 = 0;
+ /* Must set DescOwned later to avoid race with chip */
+ dummy = entry;
+ entry = tp->cur_tx++ % TX_RING_SIZE;
+
}
tp->tx_buffers[entry].skb = NULL;
@@ -1235,10 +1229,6 @@ static int tulip_uli_dm_quirk(struct pci_dev *pdev)
{
if (pdev->vendor == 0x1282 && pdev->device == 0x9102)
return 1;
- if (pdev->vendor == 0x10b9 && pdev->device == 0x5261)
- return 1;
- if (pdev->vendor == 0x10b9 && pdev->device == 0x5263)
- return 1;
return 0;
}
@@ -1680,7 +1670,6 @@ static int __devinit tulip_init_one (struct pci_dev *pdev,
switch (chip_idx) {
case DC21140:
case DM910X:
- case ULI526X:
default:
if (tp->mtable)
iowrite32(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/tulip/uli526x.c
new file mode 100644
index 000000000000..5ae22b7bc5ca
--- /dev/null
+++ b/drivers/net/tulip/uli526x.c
@@ -0,0 +1,1749 @@
+/*
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+*/
+
+#define DRV_NAME "uli526x"
+#define DRV_VERSION "0.9.3"
+#define DRV_RELDATE "2005-7-29"
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+
+#include <asm/processor.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+
+
+/* Board/System/Debug information/definition ---------------- */
+#define PCI_ULI5261_ID 0x526110B9 /* ULi M5261 ID*/
+#define PCI_ULI5263_ID 0x526310B9 /* ULi M5263 ID*/
+
+#define ULI526X_IO_SIZE 0x100
+#define TX_DESC_CNT 0x20 /* Allocated Tx descriptors */
+#define RX_DESC_CNT 0x30 /* Allocated Rx descriptors */
+#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */
+#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */
+#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT)
+#define TX_BUF_ALLOC 0x600
+#define RX_ALLOC_SIZE 0x620
+#define ULI526X_RESET 1
+#define CR0_DEFAULT 0
+#define CR6_DEFAULT 0x22200000
+#define CR7_DEFAULT 0x180c1
+#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */
+#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */
+#define MAX_PACKET_SIZE 1514
+#define ULI5261_MAX_MULTICAST 14
+#define RX_COPY_SIZE 100
+#define MAX_CHECK_PACKET 0x8000
+
+#define ULI526X_10MHF 0
+#define ULI526X_100MHF 1
+#define ULI526X_10MFD 4
+#define ULI526X_100MFD 5
+#define ULI526X_AUTO 8
+
+#define ULI526X_TXTH_72 0x400000 /* TX TH 72 byte */
+#define ULI526X_TXTH_96 0x404000 /* TX TH 96 byte */
+#define ULI526X_TXTH_128 0x0000 /* TX TH 128 byte */
+#define ULI526X_TXTH_256 0x4000 /* TX TH 256 byte */
+#define ULI526X_TXTH_512 0x8000 /* TX TH 512 byte */
+#define ULI526X_TXTH_1K 0xC000 /* TX TH 1K byte */
+
+#define ULI526X_TIMER_WUT (jiffies + HZ * 1)/* timer wakeup time : 1 second */
+#define ULI526X_TX_TIMEOUT ((16*HZ)/2) /* tx packet time-out time 8 s" */
+#define ULI526X_TX_KICK (4*HZ/2) /* tx packet Kick-out time 2 s" */
+
+#define ULI526X_DBUG(dbug_now, msg, value) if (uli526x_debug || (dbug_now)) printk(KERN_ERR DRV_NAME ": %s %lx\n", (msg), (long) (value))
+
+#define SHOW_MEDIA_TYPE(mode) printk(KERN_ERR DRV_NAME ": Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
+
+
+/* CR9 definition: SROM/MII */
+#define CR9_SROM_READ 0x4800
+#define CR9_SRCS 0x1
+#define CR9_SRCLK 0x2
+#define CR9_CRDOUT 0x8
+#define SROM_DATA_0 0x0
+#define SROM_DATA_1 0x4
+#define PHY_DATA_1 0x20000
+#define PHY_DATA_0 0x00000
+#define MDCLKH 0x10000
+
+#define PHY_POWER_DOWN 0x800
+
+#define SROM_V41_CODE 0x14
+
+#define SROM_CLK_WRITE(data, ioaddr) \
+ outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr); \
+ udelay(5); \
+ outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr); \
+ udelay(5); \
+ outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr); \
+ udelay(5);
+
+/* Structure/enum declaration ------------------------------- */
+struct tx_desc {
+ u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */
+ char *tx_buf_ptr; /* Data for us */
+ struct tx_desc *next_tx_desc;
+} __attribute__(( aligned(32) ));
+
+struct rx_desc {
+ u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */
+ struct sk_buff *rx_skb_ptr; /* Data for us */
+ struct rx_desc *next_rx_desc;
+} __attribute__(( aligned(32) ));
+
+struct uli526x_board_info {
+ u32 chip_id; /* Chip vendor/Device ID */
+ struct net_device *next_dev; /* next device */
+ struct pci_dev *pdev; /* PCI device */
+ spinlock_t lock;
+
+ long ioaddr; /* I/O base address */
+ u32 cr0_data;
+ u32 cr5_data;
+ u32 cr6_data;
+ u32 cr7_data;
+ u32 cr15_data;
+
+ /* pointer for memory physical address */
+ dma_addr_t buf_pool_dma_ptr; /* Tx buffer pool memory */
+ dma_addr_t buf_pool_dma_start; /* Tx buffer pool align dword */
+ dma_addr_t desc_pool_dma_ptr; /* descriptor pool memory */
+ dma_addr_t first_tx_desc_dma;
+ dma_addr_t first_rx_desc_dma;
+
+ /* descriptor pointer */
+ unsigned char *buf_pool_ptr; /* Tx buffer pool memory */
+ unsigned char *buf_pool_start; /* Tx buffer pool align dword */
+ unsigned char *desc_pool_ptr; /* descriptor pool memory */
+ struct tx_desc *first_tx_desc;
+ struct tx_desc *tx_insert_ptr;
+ struct tx_desc *tx_remove_ptr;
+ struct rx_desc *first_rx_desc;
+ struct rx_desc *rx_insert_ptr;
+ struct rx_desc *rx_ready_ptr; /* packet come pointer */
+ unsigned long tx_packet_cnt; /* transmitted packet count */
+ unsigned long rx_avail_cnt; /* available rx descriptor count */
+ unsigned long interval_rx_cnt; /* rx packet count a callback time */
+
+ u16 dbug_cnt;
+ u16 NIC_capability; /* NIC media capability */
+ u16 PHY_reg4; /* Saved Phyxcer register 4 value */
+
+ u8 media_mode; /* user specify media mode */
+ u8 op_mode; /* real work media mode */
+ u8 phy_addr;
+ u8 link_failed; /* Ever link failed */
+ u8 wait_reset; /* Hardware failed, need to reset */
+ struct timer_list timer;
+
+ /* System defined statistic counter */
+ struct net_device_stats stats;
+
+ /* Driver defined statistic counter */
+ unsigned long tx_fifo_underrun;
+ unsigned long tx_loss_carrier;
+ unsigned long tx_no_carrier;
+ unsigned long tx_late_collision;
+ unsigned long tx_excessive_collision;
+ unsigned long tx_jabber_timeout;
+ unsigned long reset_count;
+ unsigned long reset_cr8;
+ unsigned long reset_fatal;
+ unsigned long reset_TXtimeout;
+
+ /* NIC SROM data */
+ unsigned char srom[128];
+ u8 init;
+};
+
+enum uli526x_offsets {
+ DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20,
+ DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48,
+ DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70,
+ DCR15 = 0x78
+};
+
+enum uli526x_CR6_bits {
+ CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80,
+ CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000,
+ CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000
+};
+
+/* Global variable declaration ----------------------------- */
+static int __devinitdata printed_version;
+static char version[] __devinitdata =
+ KERN_INFO DRV_NAME ": ULi M5261/M5263 net driver, version "
+ DRV_VERSION " (" DRV_RELDATE ")\n";
+
+static int uli526x_debug;
+static unsigned char uli526x_media_mode = ULI526X_AUTO;
+static u32 uli526x_cr6_user_set;
+
+/* For module input parameter */
+static int debug;
+static u32 cr6set;
+static unsigned char mode = 8;
+
+/* function declaration ------------------------------------- */
+static int uli526x_open(struct net_device *);
+static int uli526x_start_xmit(struct sk_buff *, struct net_device *);
+static int uli526x_stop(struct net_device *);
+static struct net_device_stats * uli526x_get_stats(struct net_device *);
+static void uli526x_set_filter_mode(struct net_device *);
+static struct ethtool_ops netdev_ethtool_ops;
+static u16 read_srom_word(long, int);
+static irqreturn_t uli526x_interrupt(int, void *, struct pt_regs *);
+static void uli526x_descriptor_init(struct uli526x_board_info *, unsigned long);
+static void allocate_rx_buffer(struct uli526x_board_info *);
+static void update_cr6(u32, unsigned long);
+static void send_filter_frame(struct net_device *, int);
+static u16 phy_read(unsigned long, u8, u8, u32);
+static u16 phy_readby_cr10(unsigned long, u8, u8);
+static void phy_write(unsigned long, u8, u8, u16, u32);
+static void phy_writeby_cr10(unsigned long, u8, u8, u16);
+static void phy_write_1bit(unsigned long, u32, u32);
+static u16 phy_read_1bit(unsigned long, u32);
+static u8 uli526x_sense_speed(struct uli526x_board_info *);
+static void uli526x_process_mode(struct uli526x_board_info *);
+static void uli526x_timer(unsigned long);
+static void uli526x_rx_packet(struct net_device *, struct uli526x_board_info *);
+static void uli526x_free_tx_pkt(struct net_device *, struct uli526x_board_info *);
+static void uli526x_reuse_skb(struct uli526x_board_info *, struct sk_buff *);
+static void uli526x_dynamic_reset(struct net_device *);
+static void uli526x_free_rxbuffer(struct uli526x_board_info *);
+static void uli526x_init(struct net_device *);
+static void uli526x_set_phyxcer(struct uli526x_board_info *);
+
+/* ULI526X network board routine ---------------------------- */
+
+/*
+ * Search ULI526X board, allocate space and register it
+ */
+
+static int __devinit uli526x_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct uli526x_board_info *db; /* board information structure */
+ struct net_device *dev;
+ int i, err;
+
+ ULI526X_DBUG(0, "uli526x_init_one()", 0);
+
+ if (!printed_version++)
+ printk(version);
+
+ /* Init network device */
+ dev = alloc_etherdev(sizeof(*db));
+ if (dev == NULL)
+ return -ENOMEM;
+ SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+ printk(KERN_WARNING DRV_NAME ": 32-bit PCI DMA not available.\n");
+ err = -ENODEV;
+ goto err_out_free;
+ }
+
+ /* Enable Master/IO access, Disable memory access */
+ err = pci_enable_device(pdev);
+ if (err)
+ goto err_out_free;
+
+ if (!pci_resource_start(pdev, 0)) {
+ printk(KERN_ERR DRV_NAME ": I/O base is zero\n");
+ err = -ENODEV;
+ goto err_out_disable;
+ }
+
+ if (pci_resource_len(pdev, 0) < (ULI526X_IO_SIZE) ) {
+ printk(KERN_ERR DRV_NAME ": Allocated I/O size too small\n");
+ err = -ENODEV;
+ goto err_out_disable;
+ }
+
+ if (pci_request_regions(pdev, DRV_NAME)) {
+ printk(KERN_ERR DRV_NAME ": Failed to request PCI regions\n");
+ err = -ENODEV;
+ goto err_out_disable;
+ }
+
+ /* Init system & device */
+ db = netdev_priv(dev);
+
+ /* Allocate Tx/Rx descriptor memory */
+ db->desc_pool_ptr = pci_alloc_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, &db->desc_pool_dma_ptr);
+ if(db->desc_pool_ptr == NULL)
+ {
+ err = -ENOMEM;
+ goto err_out_nomem;
+ }
+ db->buf_pool_ptr = pci_alloc_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, &db->buf_pool_dma_ptr);
+ if(db->buf_pool_ptr == NULL)
+ {
+ err = -ENOMEM;
+ goto err_out_nomem;
+ }
+
+ db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr;
+ db->first_tx_desc_dma = db->desc_pool_dma_ptr;
+ db->buf_pool_start = db->buf_pool_ptr;
+ db->buf_pool_dma_start = db->buf_pool_dma_ptr;
+
+ db->chip_id = ent->driver_data;
+ db->ioaddr = pci_resource_start(pdev, 0);
+
+ db->pdev = pdev;
+ db->init = 1;
+
+ dev->base_addr = db->ioaddr;
+ dev->irq = pdev->irq;
+ pci_set_drvdata(pdev, dev);
+
+ /* Register some necessary functions */
+ dev->open = &uli526x_open;
+ dev->hard_start_xmit = &uli526x_start_xmit;
+ dev->stop = &uli526x_stop;
+ dev->get_stats = &uli526x_get_stats;
+ dev->set_multicast_list = &uli526x_set_filter_mode;
+ dev->ethtool_ops = &netdev_ethtool_ops;
+ spin_lock_init(&db->lock);
+
+
+ /* read 64 word srom data */
+ for (i = 0; i < 64; i++)
+ ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr, i));
+
+ /* Set Node address */
+ if(((u16 *) db->srom)[0] == 0xffff || ((u16 *) db->srom)[0] == 0) /* SROM absent, so read MAC address from ID Table */
+ {
+ outl(0x10000, db->ioaddr + DCR0); //Diagnosis mode
+ outl(0x1c0, db->ioaddr + DCR13); //Reset dianostic pointer port
+ outl(0, db->ioaddr + DCR14); //Clear reset port
+ outl(0x10, db->ioaddr + DCR14); //Reset ID Table pointer
+ outl(0, db->ioaddr + DCR14); //Clear reset port
+ outl(0, db->ioaddr + DCR13); //Clear CR13
+ outl(0x1b0, db->ioaddr + DCR13); //Select ID Table access port
+ //Read MAC address from CR14
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inl(db->ioaddr + DCR14);
+ //Read end
+ outl(0, db->ioaddr + DCR13); //Clear CR13
+ outl(0, db->ioaddr + DCR0); //Clear CR0
+ udelay(10);
+ }
+ else /*Exist SROM*/
+ {
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = db->srom[20 + i];
+ }
+ err = register_netdev (dev);
+ if (err)
+ goto err_out_res;
+
+ printk(KERN_INFO "%s: ULi M%04lx at pci%s,",dev->name,ent->driver_data >> 16,pci_name(pdev));
+
+ for (i = 0; i < 6; i++)
+ printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
+ printk(", irq %d.\n", dev->irq);
+
+ pci_set_master(pdev);
+
+ return 0;
+
+err_out_res:
+ pci_release_regions(pdev);
+err_out_nomem:
+ if(db->desc_pool_ptr)
+ pci_free_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20,
+ db->desc_pool_ptr, db->desc_pool_dma_ptr);
+
+ if(db->buf_pool_ptr != NULL)
+ pci_free_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4,
+ db->buf_pool_ptr, db->buf_pool_dma_ptr);
+err_out_disable:
+ pci_disable_device(pdev);
+err_out_free:
+ pci_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+
+ return err;
+}
+
+
+static void __devexit uli526x_remove_one (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct uli526x_board_info *db = netdev_priv(dev);
+
+ ULI526X_DBUG(0, "uli526x_remove_one()", 0);
+
+ pci_free_consistent(db->pdev, sizeof(struct tx_desc) *
+ DESC_ALL_CNT + 0x20, db->desc_pool_ptr,
+ db->desc_pool_dma_ptr);
+ pci_free_consistent(db->pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4,
+ db->buf_pool_ptr, db->buf_pool_dma_ptr);
+ unregister_netdev(dev);
+ pci_release_regions(pdev);
+ free_netdev(dev); /* free board information */
+ pci_set_drvdata(pdev, NULL);
+ pci_disable_device(pdev);
+ ULI526X_DBUG(0, "uli526x_remove_one() exit", 0);
+}
+
+
+/*
+ * Open the interface.
+ * The interface is opened whenever "ifconfig" activates it.
+ */
+
+static int uli526x_open(struct net_device *dev)
+{
+ int ret;
+ struct uli526x_board_info *db = netdev_priv(dev);
+
+ ULI526X_DBUG(0, "uli526x_open", 0);
+
+ ret = request_irq(dev->irq, &uli526x_interrupt, SA_SHIRQ, dev->name, dev);
+ if (ret)
+ return ret;
+
+ /* system variable init */
+ db->cr6_data = CR6_DEFAULT | uli526x_cr6_user_set;
+ db->tx_packet_cnt = 0;
+ db->rx_avail_cnt = 0;
+ db->link_failed = 1;
+ netif_carrier_off(dev);
+ db->wait_reset = 0;
+
+ db->NIC_capability = 0xf; /* All capability*/
+ db->PHY_reg4 = 0x1e0;
+
+ /* CR6 operation mode decision */
+ db->cr6_data |= ULI526X_TXTH_256;
+ db->cr0_data = CR0_DEFAULT;
+
+ /* Initialize ULI526X board */
+ uli526x_init(dev);
+
+ /* Active System Interface */
+ netif_wake_queue(dev);
+
+ /* set and active a timer process */
+ init_timer(&db->timer);
+ db->timer.expires = ULI526X_TIMER_WUT + HZ * 2;
+ db->timer.data = (unsigned long)dev;
+ db->timer.function = &uli526x_timer;
+ add_timer(&db->timer);
+
+ return 0;
+}
+
+
+/* Initialize ULI526X board
+ * Reset ULI526X board
+ * Initialize TX/Rx descriptor chain structure
+ * Send the set-up frame
+ * Enable Tx/Rx machine
+ */
+
+static void uli526x_init(struct net_device *dev)
+{
+ struct uli526x_board_info *db = netdev_priv(dev);
+ unsigned long ioaddr = db->ioaddr;
+ u8 phy_tmp;
+ u16 phy_value;
+ u16 phy_reg_reset;
+
+ ULI526X_DBUG(0, "uli526x_init()", 0);
+
+ /* Reset M526x MAC controller */
+ outl(ULI526X_RESET, ioaddr + DCR0); /* RESET MAC */
+ udelay(100);
+ outl(db->cr0_data, ioaddr + DCR0);
+ udelay(5);
+
+ /* Phy addr : In some boards,M5261/M5263 phy address != 1 */
+ db->phy_addr = 1;
+ for(phy_tmp=0;phy_tmp<32;phy_tmp++)
+ {
+ phy_value=phy_read(db->ioaddr,phy_tmp,3,db->chip_id);//peer add
+ if(phy_value != 0xffff&&phy_value!=0)
+ {
+ db->phy_addr = phy_tmp;
+ break;
+ }
+ }
+ if(phy_tmp == 32)
+ printk(KERN_WARNING "Can not find the phy address!!!");
+ /* Parser SROM and media mode */
+ db->media_mode = uli526x_media_mode;
+
+ /* Phyxcer capability setting */
+ phy_reg_reset = phy_read(db->ioaddr, db->phy_addr, 0, db->chip_id);
+ phy_reg_reset = (phy_reg_reset | 0x8000);
+ phy_write(db->ioaddr, db->phy_addr, 0, phy_reg_reset, db->chip_id);
+ udelay(500);
+
+ /* Process Phyxcer Media Mode */
+ uli526x_set_phyxcer(db);
+
+ /* Media Mode Process */
+ if ( !(db->media_mode & ULI526X_AUTO) )
+ db->op_mode = db->media_mode; /* Force Mode */
+
+ /* Initialize Transmit/Receive decriptor and CR3/4 */
+ uli526x_descriptor_init(db, ioaddr);
+
+ /* Init CR6 to program M526X operation */
+ update_cr6(db->cr6_data, ioaddr);
+
+ /* Send setup frame */
+ send_filter_frame(dev, dev->mc_count); /* M5261/M5263 */
+
+ /* Init CR7, interrupt active bit */
+ db->cr7_data = CR7_DEFAULT;
+ outl(db->cr7_data, ioaddr + DCR7);
+
+ /* Init CR15, Tx jabber and Rx watchdog timer */
+ outl(db->cr15_data, ioaddr + DCR15);
+
+ /* Enable ULI526X Tx/Rx function */
+ db->cr6_data |= CR6_RXSC | CR6_TXSC;
+ update_cr6(db->cr6_data, ioaddr);
+}
+
+
+/*
+ * Hardware start transmission.
+ * Send a packet to media from the upper layer.
+ */
+
+static int uli526x_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct uli526x_board_info *db = netdev_priv(dev);
+ struct tx_desc *txptr;
+ unsigned long flags;
+
+ ULI526X_DBUG(0, "uli526x_start_xmit", 0);
+
+ /* Resource flag check */
+ netif_stop_queue(dev);
+
+ /* Too large packet check */
+ if (skb->len > MAX_PACKET_SIZE) {
+ printk(KERN_ERR DRV_NAME ": big packet = %d\n", (u16)skb->len);
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ spin_lock_irqsave(&db->lock, flags);
+
+ /* No Tx resource check, it never happen nromally */
+ if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) {
+ spin_unlock_irqrestore(&db->lock, flags);
+ printk(KERN_ERR DRV_NAME ": No Tx resource %ld\n", db->tx_packet_cnt);
+ return 1;
+ }
+
+ /* Disable NIC interrupt */
+ outl(0, dev->base_addr + DCR7);
+
+ /* transmit this packet */
+ txptr = db->tx_insert_ptr;
+ memcpy(txptr->tx_buf_ptr, skb->data, skb->len);
+ txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len);
+
+ /* Point to next transmit free descriptor */
+ db->tx_insert_ptr = txptr->next_tx_desc;
+
+ /* Transmit Packet Process */
+ if ( (db->tx_packet_cnt < TX_DESC_CNT) ) {
+ txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */
+ db->tx_packet_cnt++; /* Ready to send */
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */
+ dev->trans_start = jiffies; /* saved time stamp */
+ }
+
+ /* Tx resource check */
+ if ( db->tx_packet_cnt < TX_FREE_DESC_CNT )
+ netif_wake_queue(dev);
+
+ /* Restore CR7 to enable interrupt */
+ spin_unlock_irqrestore(&db->lock, flags);
+ outl(db->cr7_data, dev->base_addr + DCR7);
+
+ /* free this SKB */
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+
+/*
+ * Stop the interface.
+ * The interface is stopped when it is brought.
+ */
+
+static int uli526x_stop(struct net_device *dev)
+{
+ struct uli526x_board_info *db = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+
+ ULI526X_DBUG(0, "uli526x_stop", 0);
+
+ /* disable system */
+ netif_stop_queue(dev);
+
+ /* deleted timer */
+ del_timer_sync(&db->timer);
+
+ /* Reset & stop ULI526X board */
+ outl(ULI526X_RESET, ioaddr + DCR0);
+ udelay(5);
+ phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id);
+
+ /* free interrupt */
+ free_irq(dev->irq, dev);
+
+ /* free allocated rx buffer */
+ uli526x_free_rxbuffer(db);
+
+#if 0
+ /* show statistic counter */
+ printk(DRV_NAME ": FU:%lx EC:%lx LC:%lx NC:%lx LOC:%lx TXJT:%lx RESET:%lx RCR8:%lx FAL:%lx TT:%lx\n",
+ db->tx_fifo_underrun, db->tx_excessive_collision,
+ db->tx_late_collision, db->tx_no_carrier, db->tx_loss_carrier,
+ db->tx_jabber_timeout, db->reset_count, db->reset_cr8,
+ db->reset_fatal, db->reset_TXtimeout);
+#endif
+
+ return 0;
+}
+
+
+/*
+ * M5261/M5263 insterrupt handler
+ * receive the packet to upper layer, free the transmitted packet
+ */
+
+static irqreturn_t uli526x_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ struct uli526x_board_info *db = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ if (!dev) {
+ ULI526X_DBUG(1, "uli526x_interrupt() without DEVICE arg", 0);
+ return IRQ_NONE;
+ }
+
+ spin_lock_irqsave(&db->lock, flags);
+ outl(0, ioaddr + DCR7);
+
+ /* Got ULI526X status */
+ db->cr5_data = inl(ioaddr + DCR5);
+ outl(db->cr5_data, ioaddr + DCR5);
+ if ( !(db->cr5_data & 0x180c1) ) {
+ spin_unlock_irqrestore(&db->lock, flags);
+ outl(db->cr7_data, ioaddr + DCR7);
+ return IRQ_HANDLED;
+ }
+
+ /* Check system status */
+ if (db->cr5_data & 0x2000) {
+ /* system bus error happen */
+ ULI526X_DBUG(1, "System bus error happen. CR5=", db->cr5_data);
+ db->reset_fatal++;
+ db->wait_reset = 1; /* Need to RESET */
+ spin_unlock_irqrestore(&db->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ /* Received the coming packet */
+ if ( (db->cr5_data & 0x40) && db->rx_avail_cnt )
+ uli526x_rx_packet(dev, db);
+
+ /* reallocate rx descriptor buffer */
+ if (db->rx_avail_cnt<RX_DESC_CNT)
+ allocate_rx_buffer(db);
+
+ /* Free the transmitted descriptor */
+ if ( db->cr5_data & 0x01)
+ uli526x_free_tx_pkt(dev, db);
+
+ /* Restore CR7 to enable interrupt mask */
+ outl(db->cr7_data, ioaddr + DCR7);
+
+ spin_unlock_irqrestore(&db->lock, flags);
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * Free TX resource after TX complete
+ */
+
+static void uli526x_free_tx_pkt(struct net_device *dev, struct uli526x_board_info * db)
+{
+ struct tx_desc *txptr;
+ u32 tdes0;
+
+ txptr = db->tx_remove_ptr;
+ while(db->tx_packet_cnt) {
+ tdes0 = le32_to_cpu(txptr->tdes0);
+ /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */
+ if (tdes0 & 0x80000000)
+ break;
+
+ /* A packet sent completed */
+ db->tx_packet_cnt--;
+ db->stats.tx_packets++;
+
+ /* Transmit statistic counter */
+ if ( tdes0 != 0x7fffffff ) {
+ /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */
+ db->stats.collisions += (tdes0 >> 3) & 0xf;
+ db->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff;
+ if (tdes0 & TDES0_ERR_MASK) {
+ db->stats.tx_errors++;
+ if (tdes0 & 0x0002) { /* UnderRun */
+ db->tx_fifo_underrun++;
+ if ( !(db->cr6_data & CR6_SFT) ) {
+ db->cr6_data = db->cr6_data | CR6_SFT;
+ update_cr6(db->cr6_data, db->ioaddr);
+ }
+ }
+ if (tdes0 & 0x0100)
+ db->tx_excessive_collision++;
+ if (tdes0 & 0x0200)
+ db->tx_late_collision++;
+ if (tdes0 & 0x0400)
+ db->tx_no_carrier++;
+ if (tdes0 & 0x0800)
+ db->tx_loss_carrier++;
+ if (tdes0 & 0x4000)
+ db->tx_jabber_timeout++;
+ }
+ }
+
+ txptr = txptr->next_tx_desc;
+ }/* End of while */
+
+ /* Update TX remove pointer to next */
+ db->tx_remove_ptr = txptr;
+
+ /* Resource available check */
+ if ( db->tx_packet_cnt < TX_WAKE_DESC_CNT )
+ netif_wake_queue(dev); /* Active upper layer, send again */
+}
+
+
+/*
+ * Receive the come packet and pass to upper layer
+ */
+
+static void uli526x_rx_packet(struct net_device *dev, struct uli526x_board_info * db)
+{
+ struct rx_desc *rxptr;
+ struct sk_buff *skb;
+ int rxlen;
+ u32 rdes0;
+
+ rxptr = db->rx_ready_ptr;
+
+ while(db->rx_avail_cnt) {
+ rdes0 = le32_to_cpu(rxptr->rdes0);
+ if (rdes0 & 0x80000000) /* packet owner check */
+ {
+ break;
+ }
+
+ db->rx_avail_cnt--;
+ db->interval_rx_cnt++;
+
+ pci_unmap_single(db->pdev, le32_to_cpu(rxptr->rdes2), RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
+ if ( (rdes0 & 0x300) != 0x300) {
+ /* A packet without First/Last flag */
+ /* reuse this SKB */
+ ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0);
+ uli526x_reuse_skb(db, rxptr->rx_skb_ptr);
+ } else {
+ /* A packet with First/Last flag */
+ rxlen = ( (rdes0 >> 16) & 0x3fff) - 4;
+
+ /* error summary bit check */
+ if (rdes0 & 0x8000) {
+ /* This is a error packet */
+ //printk(DRV_NAME ": rdes0: %lx\n", rdes0);
+ db->stats.rx_errors++;
+ if (rdes0 & 1)
+ db->stats.rx_fifo_errors++;
+ if (rdes0 & 2)
+ db->stats.rx_crc_errors++;
+ if (rdes0 & 0x80)
+ db->stats.rx_length_errors++;
+ }
+
+ if ( !(rdes0 & 0x8000) ||
+ ((db->cr6_data & CR6_PM) && (rxlen>6)) ) {
+ skb = rxptr->rx_skb_ptr;
+
+ /* Good packet, send to upper layer */
+ /* Shorst packet used new SKB */
+ if ( (rxlen < RX_COPY_SIZE) &&
+ ( (skb = dev_alloc_skb(rxlen + 2) )
+ != NULL) ) {
+ /* size less than COPY_SIZE, allocate a rxlen SKB */
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* 16byte align */
+ memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->tail, rxlen);
+ uli526x_reuse_skb(db, rxptr->rx_skb_ptr);
+ } else {
+ skb->dev = dev;
+ skb_put(skb, rxlen);
+ }
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ db->stats.rx_packets++;
+ db->stats.rx_bytes += rxlen;
+
+ } else {
+ /* Reuse SKB buffer when the packet is error */
+ ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0);
+ uli526x_reuse_skb(db, rxptr->rx_skb_ptr);
+ }
+ }
+
+ rxptr = rxptr->next_rx_desc;
+ }
+
+ db->rx_ready_ptr = rxptr;
+}
+
+
+/*
+ * Get statistics from driver.
+ */
+
+static struct net_device_stats * uli526x_get_stats(struct net_device *dev)
+{
+ struct uli526x_board_info *db = netdev_priv(dev);
+
+ ULI526X_DBUG(0, "uli526x_get_stats", 0);
+ return &db->stats;
+}
+
+
+/*
+ * Set ULI526X multicast address
+ */
+
+static void uli526x_set_filter_mode(struct net_device * dev)
+{
+ struct uli526x_board_info *db = dev->priv;
+ unsigned long flags;
+
+ ULI526X_DBUG(0, "uli526x_set_filter_mode()", 0);
+ spin_lock_irqsave(&db->lock, flags);
+
+ if (dev->flags & IFF_PROMISC) {
+ ULI526X_DBUG(0, "Enable PROM Mode", 0);
+ db->cr6_data |= CR6_PM | CR6_PBF;
+ update_cr6(db->cr6_data, db->ioaddr);
+ spin_unlock_irqrestore(&db->lock, flags);
+ return;
+ }
+
+ if (dev->flags & IFF_ALLMULTI || dev->mc_count > ULI5261_MAX_MULTICAST) {
+ ULI526X_DBUG(0, "Pass all multicast address", dev->mc_count);
+ db->cr6_data &= ~(CR6_PM | CR6_PBF);
+ db->cr6_data |= CR6_PAM;
+ spin_unlock_irqrestore(&db->lock, flags);
+ return;
+ }
+
+ ULI526X_DBUG(0, "Set multicast address", dev->mc_count);
+ send_filter_frame(dev, dev->mc_count); /* M5261/M5263 */
+ spin_unlock_irqrestore(&db->lock, flags);
+}
+
+static void
+ULi_ethtool_gset(struct uli526x_board_info *db, struct ethtool_cmd *ecmd)
+{
+ ecmd->supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg |
+ SUPPORTED_MII);
+
+ ecmd->advertising = (ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full |
+ ADVERTISED_Autoneg |
+ ADVERTISED_MII);
+
+
+ ecmd->port = PORT_MII;
+ ecmd->phy_address = db->phy_addr;
+
+ ecmd->transceiver = XCVR_EXTERNAL;
+
+ ecmd->speed = 10;
+ ecmd->duplex = DUPLEX_HALF;
+
+ if(db->op_mode==ULI526X_100MHF || db->op_mode==ULI526X_100MFD)
+ {
+ ecmd->speed = 100;
+ }
+ if(db->op_mode==ULI526X_10MFD || db->op_mode==ULI526X_100MFD)
+ {
+ ecmd->duplex = DUPLEX_FULL;
+ }
+ if(db->link_failed)
+ {
+ ecmd->speed = -1;
+ ecmd->duplex = -1;
+ }
+
+ if (db->media_mode & ULI526X_AUTO)
+ {
+ ecmd->autoneg = AUTONEG_ENABLE;
+ }
+}
+
+static void netdev_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct uli526x_board_info *np = netdev_priv(dev);
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ if (np->pdev)
+ strcpy(info->bus_info, pci_name(np->pdev));
+ else
+ sprintf(info->bus_info, "EISA 0x%lx %d",
+ dev->base_addr, dev->irq);
+}
+
+static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) {
+ struct uli526x_board_info *np = netdev_priv(dev);
+
+ ULi_ethtool_gset(np, cmd);
+
+ return 0;
+}
+
+static u32 netdev_get_link(struct net_device *dev) {
+ struct uli526x_board_info *np = netdev_priv(dev);
+
+ if(np->link_failed)
+ return 0;
+ else
+ return 1;
+}
+
+static void uli526x_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ wol->supported = WAKE_PHY | WAKE_MAGIC;
+ wol->wolopts = 0;
+}
+
+static struct ethtool_ops netdev_ethtool_ops = {
+ .get_drvinfo = netdev_get_drvinfo,
+ .get_settings = netdev_get_settings,
+ .get_link = netdev_get_link,
+ .get_wol = uli526x_get_wol,
+};
+
+/*
+ * A periodic timer routine
+ * Dynamic media sense, allocate Rx buffer...
+ */
+
+static void uli526x_timer(unsigned long data)
+{
+ u32 tmp_cr8;
+ unsigned char tmp_cr12=0;
+ struct net_device *dev = (struct net_device *) data;
+ struct uli526x_board_info *db = netdev_priv(dev);
+ unsigned long flags;
+ u8 TmpSpeed=10;
+
+ //ULI526X_DBUG(0, "uli526x_timer()", 0);
+ spin_lock_irqsave(&db->lock, flags);
+
+
+ /* Dynamic reset ULI526X : system error or transmit time-out */
+ tmp_cr8 = inl(db->ioaddr + DCR8);
+ if ( (db->interval_rx_cnt==0) && (tmp_cr8) ) {
+ db->reset_cr8++;
+ db->wait_reset = 1;
+ }
+ db->interval_rx_cnt = 0;
+
+ /* TX polling kick monitor */
+ if ( db->tx_packet_cnt &&
+ time_after(jiffies, dev->trans_start + ULI526X_TX_KICK) ) {
+ outl(0x1, dev->base_addr + DCR1); // Tx polling again
+
+ // TX Timeout
+ if ( time_after(jiffies, dev->trans_start + ULI526X_TX_TIMEOUT) ) {
+ db->reset_TXtimeout++;
+ db->wait_reset = 1;
+ printk( "%s: Tx timeout - resetting\n",
+ dev->name);
+ }
+ }
+
+ if (db->wait_reset) {
+ ULI526X_DBUG(0, "Dynamic Reset device", db->tx_packet_cnt);
+ db->reset_count++;
+ uli526x_dynamic_reset(dev);
+ db->timer.expires = ULI526X_TIMER_WUT;
+ add_timer(&db->timer);
+ spin_unlock_irqrestore(&db->lock, flags);
+ return;
+ }
+
+ /* Link status check, Dynamic media type change */
+ if((phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id) & 0x01e0)!=0)
+ tmp_cr12 = 3;
+
+ if ( !(tmp_cr12 & 0x3) && !db->link_failed ) {
+ /* Link Failed */
+ ULI526X_DBUG(0, "Link Failed", tmp_cr12);
+ netif_carrier_off(dev);
+ printk(KERN_INFO "uli526x: %s NIC Link is Down\n",dev->name);
+ db->link_failed = 1;
+
+ /* For Force 10/100M Half/Full mode: Enable Auto-Nego mode */
+ /* AUTO don't need */
+ if ( !(db->media_mode & 0x8) )
+ phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id);
+
+ /* AUTO mode, if INT phyxcer link failed, select EXT device */
+ if (db->media_mode & ULI526X_AUTO) {
+ db->cr6_data&=~0x00000200; /* bit9=0, HD mode */
+ update_cr6(db->cr6_data, db->ioaddr);
+ }
+ } else
+ if ((tmp_cr12 & 0x3) && db->link_failed) {
+ ULI526X_DBUG(0, "Link link OK", tmp_cr12);
+ db->link_failed = 0;
+
+ /* Auto Sense Speed */
+ if ( (db->media_mode & ULI526X_AUTO) &&
+ uli526x_sense_speed(db) )
+ db->link_failed = 1;
+ uli526x_process_mode(db);
+
+ if(db->link_failed==0)
+ {
+ if(db->op_mode==ULI526X_100MHF || db->op_mode==ULI526X_100MFD)
+ {
+ TmpSpeed = 100;
+ }
+ if(db->op_mode==ULI526X_10MFD || db->op_mode==ULI526X_100MFD)
+ {
+ printk(KERN_INFO "uli526x: %s NIC Link is Up %d Mbps Full duplex\n",dev->name,TmpSpeed);
+ }
+ else
+ {
+ printk(KERN_INFO "uli526x: %s NIC Link is Up %d Mbps Half duplex\n",dev->name,TmpSpeed);
+ }
+ netif_carrier_on(dev);
+ }
+ /* SHOW_MEDIA_TYPE(db->op_mode); */
+ }
+ else if(!(tmp_cr12 & 0x3) && db->link_failed)
+ {
+ if(db->init==1)
+ {
+ printk(KERN_INFO "uli526x: %s NIC Link is Down\n",dev->name);
+ netif_carrier_off(dev);
+ }
+ }
+ db->init=0;
+
+ /* Timer active again */
+ db->timer.expires = ULI526X_TIMER_WUT;
+ add_timer(&db->timer);
+ spin_unlock_irqrestore(&db->lock, flags);
+}
+
+
+/*
+ * Dynamic reset the ULI526X board
+ * Stop ULI526X board
+ * Free Tx/Rx allocated memory
+ * Reset ULI526X board
+ * Re-initialize ULI526X board
+ */
+
+static void uli526x_dynamic_reset(struct net_device *dev)
+{
+ struct uli526x_board_info *db = netdev_priv(dev);
+
+ ULI526X_DBUG(0, "uli526x_dynamic_reset()", 0);
+
+ /* Sopt MAC controller */
+ db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */
+ update_cr6(db->cr6_data, dev->base_addr);
+ outl(0, dev->base_addr + DCR7); /* Disable Interrupt */
+ outl(inl(dev->base_addr + DCR5), dev->base_addr + DCR5);
+
+ /* Disable upper layer interface */
+ netif_stop_queue(dev);
+
+ /* Free Rx Allocate buffer */
+ uli526x_free_rxbuffer(db);
+
+ /* system variable init */
+ db->tx_packet_cnt = 0;
+ db->rx_avail_cnt = 0;
+ db->link_failed = 1;
+ db->init=1;
+ db->wait_reset = 0;
+
+ /* Re-initialize ULI526X board */
+ uli526x_init(dev);
+
+ /* Restart upper layer interface */
+ netif_wake_queue(dev);
+}
+
+
+/*
+ * free all allocated rx buffer
+ */
+
+static void uli526x_free_rxbuffer(struct uli526x_board_info * db)
+{
+ ULI526X_DBUG(0, "uli526x_free_rxbuffer()", 0);
+
+ /* free allocated rx buffer */
+ while (db->rx_avail_cnt) {
+ dev_kfree_skb(db->rx_ready_ptr->rx_skb_ptr);
+ db->rx_ready_ptr = db->rx_ready_ptr->next_rx_desc;
+ db->rx_avail_cnt--;
+ }
+}
+
+
+/*
+ * Reuse the SK buffer
+ */
+
+static void uli526x_reuse_skb(struct uli526x_board_info *db, struct sk_buff * skb)
+{
+ struct rx_desc *rxptr = db->rx_insert_ptr;
+
+ if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) {
+ rxptr->rx_skb_ptr = skb;
+ rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) );
+ wmb();
+ rxptr->rdes0 = cpu_to_le32(0x80000000);
+ db->rx_avail_cnt++;
+ db->rx_insert_ptr = rxptr->next_rx_desc;
+ } else
+ ULI526X_DBUG(0, "SK Buffer reuse method error", db->rx_avail_cnt);
+}
+
+
+/*
+ * Initialize transmit/Receive descriptor
+ * Using Chain structure, and allocate Tx/Rx buffer
+ */
+
+static void uli526x_descriptor_init(struct uli526x_board_info *db, unsigned long ioaddr)
+{
+ struct tx_desc *tmp_tx;
+ struct rx_desc *tmp_rx;
+ unsigned char *tmp_buf;
+ dma_addr_t tmp_tx_dma, tmp_rx_dma;
+ dma_addr_t tmp_buf_dma;
+ int i;
+
+ ULI526X_DBUG(0, "uli526x_descriptor_init()", 0);
+
+ /* tx descriptor start pointer */
+ db->tx_insert_ptr = db->first_tx_desc;
+ db->tx_remove_ptr = db->first_tx_desc;
+ outl(db->first_tx_desc_dma, ioaddr + DCR4); /* TX DESC address */
+
+ /* rx descriptor start pointer */
+ db->first_rx_desc = (void *)db->first_tx_desc + sizeof(struct tx_desc) * TX_DESC_CNT;
+ db->first_rx_desc_dma = db->first_tx_desc_dma + sizeof(struct tx_desc) * TX_DESC_CNT;
+ db->rx_insert_ptr = db->first_rx_desc;
+ db->rx_ready_ptr = db->first_rx_desc;
+ outl(db->first_rx_desc_dma, ioaddr + DCR3); /* RX DESC address */
+
+ /* Init Transmit chain */
+ tmp_buf = db->buf_pool_start;
+ tmp_buf_dma = db->buf_pool_dma_start;
+ tmp_tx_dma = db->first_tx_desc_dma;
+ for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) {
+ tmp_tx->tx_buf_ptr = tmp_buf;
+ tmp_tx->tdes0 = cpu_to_le32(0);
+ tmp_tx->tdes1 = cpu_to_le32(0x81000000); /* IC, chain */
+ tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma);
+ tmp_tx_dma += sizeof(struct tx_desc);
+ tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma);
+ tmp_tx->next_tx_desc = tmp_tx + 1;
+ tmp_buf = tmp_buf + TX_BUF_ALLOC;
+ tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC;
+ }
+ (--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma);
+ tmp_tx->next_tx_desc = db->first_tx_desc;
+
+ /* Init Receive descriptor chain */
+ tmp_rx_dma=db->first_rx_desc_dma;
+ for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) {
+ tmp_rx->rdes0 = cpu_to_le32(0);
+ tmp_rx->rdes1 = cpu_to_le32(0x01000600);
+ tmp_rx_dma += sizeof(struct rx_desc);
+ tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma);
+ tmp_rx->next_rx_desc = tmp_rx + 1;
+ }
+ (--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma);
+ tmp_rx->next_rx_desc = db->first_rx_desc;
+
+ /* pre-allocate Rx buffer */
+ allocate_rx_buffer(db);
+}
+
+
+/*
+ * Update CR6 value
+ * Firstly stop ULI526X, then written value and start
+ */
+
+static void update_cr6(u32 cr6_data, unsigned long ioaddr)
+{
+
+ outl(cr6_data, ioaddr + DCR6);
+ udelay(5);
+}
+
+
+/*
+ * Send a setup frame for M5261/M5263
+ * This setup frame initialize ULI526X address filter mode
+ */
+
+static void send_filter_frame(struct net_device *dev, int mc_cnt)
+{
+ struct uli526x_board_info *db = netdev_priv(dev);
+ struct dev_mc_list *mcptr;
+ struct tx_desc *txptr;
+ u16 * addrptr;
+ u32 * suptr;
+ int i;
+
+ ULI526X_DBUG(0, "send_filter_frame()", 0);
+
+ txptr = db->tx_insert_ptr;
+ suptr = (u32 *) txptr->tx_buf_ptr;
+
+ /* Node address */
+ addrptr = (u16 *) dev->dev_addr;
+ *suptr++ = addrptr[0];
+ *suptr++ = addrptr[1];
+ *suptr++ = addrptr[2];
+
+ /* broadcast address */
+ *suptr++ = 0xffff;
+ *suptr++ = 0xffff;
+ *suptr++ = 0xffff;
+
+ /* fit the multicast address */
+ for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
+ addrptr = (u16 *) mcptr->dmi_addr;
+ *suptr++ = addrptr[0];
+ *suptr++ = addrptr[1];
+ *suptr++ = addrptr[2];
+ }
+
+ for (; i<14; i++) {
+ *suptr++ = 0xffff;
+ *suptr++ = 0xffff;
+ *suptr++ = 0xffff;
+ }
+
+ /* prepare the setup frame */
+ db->tx_insert_ptr = txptr->next_tx_desc;
+ txptr->tdes1 = cpu_to_le32(0x890000c0);
+
+ /* Resource Check and Send the setup packet */
+ if (db->tx_packet_cnt < TX_DESC_CNT) {
+ /* Resource Empty */
+ db->tx_packet_cnt++;
+ txptr->tdes0 = cpu_to_le32(0x80000000);
+ update_cr6(db->cr6_data | 0x2000, dev->base_addr);
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */
+ update_cr6(db->cr6_data, dev->base_addr);
+ dev->trans_start = jiffies;
+ } else
+ printk(KERN_ERR DRV_NAME ": No Tx resource - Send_filter_frame!\n");
+}
+
+
+/*
+ * Allocate rx buffer,
+ * As possible as allocate maxiumn Rx buffer
+ */
+
+static void allocate_rx_buffer(struct uli526x_board_info *db)
+{
+ struct rx_desc *rxptr;
+ struct sk_buff *skb;
+
+ rxptr = db->rx_insert_ptr;
+
+ while(db->rx_avail_cnt < RX_DESC_CNT) {
+ if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL )
+ break;
+ rxptr->rx_skb_ptr = skb; /* FIXME (?) */
+ rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) );
+ wmb();
+ rxptr->rdes0 = cpu_to_le32(0x80000000);
+ rxptr = rxptr->next_rx_desc;
+ db->rx_avail_cnt++;
+ }
+
+ db->rx_insert_ptr = rxptr;
+}
+
+
+/*
+ * Read one word data from the serial ROM
+ */
+
+static u16 read_srom_word(long ioaddr, int offset)
+{
+ int i;
+ u16 srom_data = 0;
+ long cr9_ioaddr = ioaddr + DCR9;
+
+ outl(CR9_SROM_READ, cr9_ioaddr);
+ outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+ /* Send the Read Command 110b */
+ SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+ SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+ SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr);
+
+ /* Send the offset */
+ for (i = 5; i >= 0; i--) {
+ srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0;
+ SROM_CLK_WRITE(srom_data, cr9_ioaddr);
+ }
+
+ outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+ for (i = 16; i > 0; i--) {
+ outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr);
+ udelay(5);
+ srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0);
+ outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+ udelay(5);
+ }
+
+ outl(CR9_SROM_READ, cr9_ioaddr);
+ return srom_data;
+}
+
+
+/*
+ * Auto sense the media mode
+ */
+
+static u8 uli526x_sense_speed(struct uli526x_board_info * db)
+{
+ u8 ErrFlag = 0;
+ u16 phy_mode;
+
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id);
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id);
+
+ if ( (phy_mode & 0x24) == 0x24 ) {
+
+ phy_mode = ((phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id) & 0x01e0)<<7);
+ if(phy_mode&0x8000)
+ phy_mode = 0x8000;
+ else if(phy_mode&0x4000)
+ phy_mode = 0x4000;
+ else if(phy_mode&0x2000)
+ phy_mode = 0x2000;
+ else
+ phy_mode = 0x1000;
+
+ /* printk(DRV_NAME ": Phy_mode %x ",phy_mode); */
+ switch (phy_mode) {
+ case 0x1000: db->op_mode = ULI526X_10MHF; break;
+ case 0x2000: db->op_mode = ULI526X_10MFD; break;
+ case 0x4000: db->op_mode = ULI526X_100MHF; break;
+ case 0x8000: db->op_mode = ULI526X_100MFD; break;
+ default: db->op_mode = ULI526X_10MHF; ErrFlag = 1; break;
+ }
+ } else {
+ db->op_mode = ULI526X_10MHF;
+ ULI526X_DBUG(0, "Link Failed :", phy_mode);
+ ErrFlag = 1;
+ }
+
+ return ErrFlag;
+}
+
+
+/*
+ * Set 10/100 phyxcer capability
+ * AUTO mode : phyxcer register4 is NIC capability
+ * Force mode: phyxcer register4 is the force media
+ */
+
+static void uli526x_set_phyxcer(struct uli526x_board_info *db)
+{
+ u16 phy_reg;
+
+ /* Phyxcer capability setting */
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x01e0;
+
+ if (db->media_mode & ULI526X_AUTO) {
+ /* AUTO Mode */
+ phy_reg |= db->PHY_reg4;
+ } else {
+ /* Force Mode */
+ switch(db->media_mode) {
+ case ULI526X_10MHF: phy_reg |= 0x20; break;
+ case ULI526X_10MFD: phy_reg |= 0x40; break;
+ case ULI526X_100MHF: phy_reg |= 0x80; break;
+ case ULI526X_100MFD: phy_reg |= 0x100; break;
+ }
+
+ }
+
+ /* Write new capability to Phyxcer Reg4 */
+ if ( !(phy_reg & 0x01e0)) {
+ phy_reg|=db->PHY_reg4;
+ db->media_mode|=ULI526X_AUTO;
+ }
+ phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id);
+
+ /* Restart Auto-Negotiation */
+ phy_write(db->ioaddr, db->phy_addr, 0, 0x1200, db->chip_id);
+ udelay(50);
+}
+
+
+/*
+ * Process op-mode
+ AUTO mode : PHY controller in Auto-negotiation Mode
+ * Force mode: PHY controller in force mode with HUB
+ * N-way force capability with SWITCH
+ */
+
+static void uli526x_process_mode(struct uli526x_board_info *db)
+{
+ u16 phy_reg;
+
+ /* Full Duplex Mode Check */
+ if (db->op_mode & 0x4)
+ db->cr6_data |= CR6_FDM; /* Set Full Duplex Bit */
+ else
+ db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */
+
+ update_cr6(db->cr6_data, db->ioaddr);
+
+ /* 10/100M phyxcer force mode need */
+ if ( !(db->media_mode & 0x8)) {
+ /* Forece Mode */
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 6, db->chip_id);
+ if ( !(phy_reg & 0x1) ) {
+ /* parter without N-Way capability */
+ phy_reg = 0x0;
+ switch(db->op_mode) {
+ case ULI526X_10MHF: phy_reg = 0x0; break;
+ case ULI526X_10MFD: phy_reg = 0x100; break;
+ case ULI526X_100MHF: phy_reg = 0x2000; break;
+ case ULI526X_100MFD: phy_reg = 0x2100; break;
+ }
+ phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id);
+ phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id);
+ }
+ }
+}
+
+
+/*
+ * Write a word to Phy register
+ */
+
+static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id)
+{
+ u16 i;
+ unsigned long ioaddr;
+
+ if(chip_id == PCI_ULI5263_ID)
+ {
+ phy_writeby_cr10(iobase, phy_addr, offset, phy_data);
+ return;
+ }
+ /* M5261/M5263 Chip */
+ ioaddr = iobase + DCR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0, chip_id);
+ phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+
+ /* Send write command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0, chip_id);
+ phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+
+ /* Send Phy address */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0, chip_id);
+
+ /* Send register address */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0, chip_id);
+
+ /* written trasnition */
+ phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+ phy_write_1bit(ioaddr, PHY_DATA_0, chip_id);
+
+ /* Write a word data to PHY controller */
+ for ( i = 0x8000; i > 0; i >>= 1)
+ phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0, chip_id);
+
+}
+
+
+/*
+ * Read a word data from phy register
+ */
+
+static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id)
+{
+ int i;
+ u16 phy_data;
+ unsigned long ioaddr;
+
+ if(chip_id == PCI_ULI5263_ID)
+ return phy_readby_cr10(iobase, phy_addr, offset);
+ /* M5261/M5263 Chip */
+ ioaddr = iobase + DCR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0, chip_id);
+ phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+
+ /* Send read command(10) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+ phy_write_1bit(ioaddr, PHY_DATA_0, chip_id);
+
+ /* Send Phy address */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0, chip_id);
+
+ /* Send register address */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0, chip_id);
+
+ /* Skip transition state */
+ phy_read_1bit(ioaddr, chip_id);
+
+ /* read 16bit data */
+ for (phy_data = 0, i = 0; i < 16; i++) {
+ phy_data <<= 1;
+ phy_data |= phy_read_1bit(ioaddr, chip_id);
+ }
+
+ return phy_data;
+}
+
+static u16 phy_readby_cr10(unsigned long iobase, u8 phy_addr, u8 offset)
+{
+ unsigned long ioaddr,cr10_value;
+
+ ioaddr = iobase + DCR10;
+ cr10_value = phy_addr;
+ cr10_value = (cr10_value<<5) + offset;
+ cr10_value = (cr10_value<<16) + 0x08000000;
+ outl(cr10_value,ioaddr);
+ udelay(1);
+ while(1)
+ {
+ cr10_value = inl(ioaddr);
+ if(cr10_value&0x10000000)
+ break;
+ }
+ return (cr10_value&0x0ffff);
+}
+
+static void phy_writeby_cr10(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data)
+{
+ unsigned long ioaddr,cr10_value;
+
+ ioaddr = iobase + DCR10;
+ cr10_value = phy_addr;
+ cr10_value = (cr10_value<<5) + offset;
+ cr10_value = (cr10_value<<16) + 0x04000000 + phy_data;
+ outl(cr10_value,ioaddr);
+ udelay(1);
+}
+/*
+ * Write one bit data to Phy Controller
+ */
+
+static void phy_write_1bit(unsigned long ioaddr, u32 phy_data, u32 chip_id)
+{
+ outl(phy_data , ioaddr); /* MII Clock Low */
+ udelay(1);
+ outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */
+ udelay(1);
+ outl(phy_data , ioaddr); /* MII Clock Low */
+ udelay(1);
+}
+
+
+/*
+ * Read one bit phy data from PHY controller
+ */
+
+static u16 phy_read_1bit(unsigned long ioaddr, u32 chip_id)
+{
+ u16 phy_data;
+
+ outl(0x50000 , ioaddr);
+ udelay(1);
+ phy_data = ( inl(ioaddr) >> 19 ) & 0x1;
+ outl(0x40000 , ioaddr);
+ udelay(1);
+
+ return phy_data;
+}
+
+
+static struct pci_device_id uli526x_pci_tbl[] = {
+ { 0x10B9, 0x5261, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ULI5261_ID },
+ { 0x10B9, 0x5263, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ULI5263_ID },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, uli526x_pci_tbl);
+
+
+static struct pci_driver uli526x_driver = {
+ .name = "uli526x",
+ .id_table = uli526x_pci_tbl,
+ .probe = uli526x_init_one,
+ .remove = __devexit_p(uli526x_remove_one),
+};
+
+MODULE_AUTHOR("Peer Chen, peer.chen@uli.com.tw");
+MODULE_DESCRIPTION("ULi M5261/M5263 fast ethernet driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(debug, "i");
+MODULE_PARM(mode, "i");
+MODULE_PARM(cr6set, "i");
+MODULE_PARM_DESC(debug, "ULi M5261/M5263 enable debugging (0-1)");
+MODULE_PARM_DESC(mode, "ULi M5261/M5263: Bit 0: 10/100Mbps, bit 2: duplex, bit 8: HomePNA");
+
+/* Description:
+ * when user used insmod to add module, system invoked init_module()
+ * to register the services.
+ */
+
+static int __init uli526x_init_module(void)
+{
+ int rc;
+
+ printk(version);
+ printed_version = 1;
+
+ ULI526X_DBUG(0, "init_module() ", debug);
+
+ if (debug)
+ uli526x_debug = debug; /* set debug flag */
+ if (cr6set)
+ uli526x_cr6_user_set = cr6set;
+
+ switch(mode) {
+ case ULI526X_10MHF:
+ case ULI526X_100MHF:
+ case ULI526X_10MFD:
+ case ULI526X_100MFD:
+ uli526x_media_mode = mode;
+ break;
+ default:uli526x_media_mode = ULI526X_AUTO;
+ break;
+ }
+
+ rc = pci_module_init(&uli526x_driver);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+
+/*
+ * Description:
+ * when user used rmmod to delete module, system invoked clean_module()
+ * to un-register all registered services.
+ */
+
+static void __exit uli526x_cleanup_module(void)
+{
+ ULI526X_DBUG(0, "uli526x_clean_module() ", debug);
+ pci_unregister_driver(&uli526x_driver);
+}
+
+module_init(uli526x_init_module);
+module_exit(uli526x_cleanup_module);
diff --git a/drivers/net/wan/hdlc_generic.c b/drivers/net/wan/hdlc_generic.c
index a63f6a2cc4f7..cdd4c09c2d90 100644
--- a/drivers/net/wan/hdlc_generic.c
+++ b/drivers/net/wan/hdlc_generic.c
@@ -61,7 +61,7 @@ static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *p)
+ struct packet_type *p, struct net_device *orig_dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
if (hdlc->proto.netif_rx)
diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c
index 7f2e3653c5e5..6c302e9dbca2 100644
--- a/drivers/net/wan/lapbether.c
+++ b/drivers/net/wan/lapbether.c
@@ -86,7 +86,7 @@ static __inline__ int dev_is_ethdev(struct net_device *dev)
/*
* Receive a LAPB frame via an ethernet interface.
*/
-static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype)
+static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev)
{
int len, err;
struct lapbethdev *lapbeth;
diff --git a/drivers/net/wan/sdla_fr.c b/drivers/net/wan/sdla_fr.c
index c5f5e62aab8b..0497dbdb8631 100644
--- a/drivers/net/wan/sdla_fr.c
+++ b/drivers/net/wan/sdla_fr.c
@@ -445,7 +445,7 @@ void s508_s514_unlock(sdla_t *card, unsigned long *smp_flags);
void s508_s514_lock(sdla_t *card, unsigned long *smp_flags);
unsigned short calc_checksum (char *, int);
-static int setup_fr_header(struct sk_buff** skb,
+static int setup_fr_header(struct sk_buff *skb,
struct net_device* dev, char op_mode);
@@ -1372,7 +1372,7 @@ static int if_send(struct sk_buff* skb, struct net_device* dev)
/* Move the if_header() code to here. By inserting frame
* relay header in if_header() we would break the
* tcpdump and other packet sniffers */
- chan->fr_header_len = setup_fr_header(&skb,dev,chan->common.usedby);
+ chan->fr_header_len = setup_fr_header(skb,dev,chan->common.usedby);
if (chan->fr_header_len < 0 ){
++chan->ifstats.tx_dropped;
++card->wandev.stats.tx_dropped;
@@ -1597,8 +1597,6 @@ static int setup_for_delayed_transmit(struct net_device* dev,
return 1;
}
- skb_unlink(skb);
-
chan->transmit_length = len;
chan->delay_skb = skb;
@@ -4871,18 +4869,15 @@ static void unconfig_fr (sdla_t *card)
}
}
-static int setup_fr_header(struct sk_buff **skb_orig, struct net_device* dev,
+static int setup_fr_header(struct sk_buff *skb, struct net_device* dev,
char op_mode)
{
- struct sk_buff *skb = *skb_orig;
fr_channel_t *chan=dev->priv;
- if (op_mode == WANPIPE){
-
+ if (op_mode == WANPIPE) {
chan->fr_header[0]=Q922_UI;
switch (htons(skb->protocol)){
-
case ETH_P_IP:
chan->fr_header[1]=NLPID_IP;
break;
@@ -4894,16 +4889,14 @@ static int setup_fr_header(struct sk_buff **skb_orig, struct net_device* dev,
}
/* If we are in bridging mode, we must apply
- * an Ethernet header */
- if (op_mode == BRIDGE || op_mode == BRIDGE_NODE){
-
-
+ * an Ethernet header
+ */
+ if (op_mode == BRIDGE || op_mode == BRIDGE_NODE) {
/* Encapsulate the packet as a bridged Ethernet frame. */
#ifdef DEBUG
printk(KERN_INFO "%s: encapsulating skb for frame relay\n",
dev->name);
#endif
-
chan->fr_header[0] = 0x03;
chan->fr_header[1] = 0x00;
chan->fr_header[2] = 0x80;
@@ -4916,7 +4909,6 @@ static int setup_fr_header(struct sk_buff **skb_orig, struct net_device* dev,
/* Yuck. */
skb->protocol = ETH_P_802_3;
return 8;
-
}
return 0;
diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c
index 84b65c60c799..f58c794a963a 100644
--- a/drivers/net/wan/syncppp.c
+++ b/drivers/net/wan/syncppp.c
@@ -1447,7 +1447,7 @@ static void sppp_print_bytes (u_char *p, u16 len)
* after interrupt servicing to process frames queued via netif_rx.
*/
-static int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p)
+static int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p, struct net_device *orig_dev)
{
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
return NET_RX_DROP;
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index ec3f75a030d2..dd7dbf7b14d4 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -137,6 +137,110 @@ config PCMCIA_RAYCS
comment "Wireless 802.11b ISA/PCI cards support"
depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA)
+config IPW2100
+ tristate "Intel PRO/Wireless 2100 Network Connection"
+ depends on NET_RADIO && PCI && IEEE80211
+ select FW_LOADER
+ ---help---
+ A driver for the Intel PRO/Wireless 2100 Network
+ Connection 802.11b wireless network adapter.
+
+ See <file:Documentation/networking/README.ipw2100> for information on
+ the capabilities currently enabled in this driver and for tips
+ for debugging issues and problems.
+
+ In order to use this driver, you will need a firmware image for it.
+ You can obtain the firmware from
+ <http://ipw2100.sf.net/>. Once you have the firmware image, you
+ will need to place it in /etc/firmware.
+
+ You will also very likely need the Wireless Tools in order to
+ configure your card:
+
+ <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
+
+ If you want to compile the driver as a module ( = code which can be
+ inserted in and remvoed from the running kernel whenever you want),
+ say M here and read <file:Documentation/modules.txt>. The module
+ will be called ipw2100.ko.
+
+config IPW2100_MONITOR
+ bool "Enable promiscuous mode"
+ depends on IPW2100
+ ---help---
+ Enables promiscuous/monitor mode support for the ipw2100 driver.
+ With this feature compiled into the driver, you can switch to
+ promiscuous mode via the Wireless Tool's Monitor mode. While in this
+ mode, no packets can be sent.
+
+config IPW_DEBUG
+ bool "Enable full debugging output in IPW2100 module."
+ depends on IPW2100
+ ---help---
+ This option will enable debug tracing output for the IPW2100.
+
+ This will result in the kernel module being ~60k larger. You can
+ control which debug output is sent to the kernel log by setting the
+ value in
+
+ /sys/bus/pci/drivers/ipw2100/debug_level
+
+ This entry will only exist if this option is enabled.
+
+ If you are not trying to debug or develop the IPW2100 driver, you
+ most likely want to say N here.
+
+config IPW2200
+ tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection"
+ depends on IEEE80211 && PCI
+ select FW_LOADER
+ ---help---
+ A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network
+ Connection adapters.
+
+ See <file:Documentation/networking/README.ipw2200> for
+ information on the capabilities currently enabled in this
+ driver and for tips for debugging issues and problems.
+
+ In order to use this driver, you will need a firmware image for it.
+ You can obtain the firmware from
+ <http://ipw2200.sf.net/>. See the above referenced README.ipw2200
+ for information on where to install the firmare images.
+
+ You will also very likely need the Wireless Tools in order to
+ configure your card:
+
+ <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
+
+ If you want to compile the driver as a module ( = code which can be
+ inserted in and remvoed from the running kernel whenever you want),
+ say M here and read <file:Documentation/modules.txt>. The module
+ will be called ipw2200.ko.
+
+config IPW_DEBUG
+ bool "Enable full debugging output in IPW2200 module."
+ depends on IPW2200
+ ---help---
+ This option will enable debug tracing output for the IPW2200.
+
+ This will result in the kernel module being ~100k larger. You can
+ control which debug output is sent to the kernel log by setting the
+ value in
+
+ /sys/bus/pci/drivers/ipw2200/debug_level
+
+ This entry will only exist if this option is enabled.
+
+ To set a value, simply echo an 8-byte hex value to the same file:
+
+ % echo 0x00000FFO > /sys/bus/pci/drivers/ipw2200/debug_level
+
+ You can find the list of debug mask values in
+ drivers/net/wireless/ipw2200.h
+
+ If you are not trying to debug or develop the IPW2200 driver, you
+ most likely want to say N here.
+
config AIRO
tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
depends on NET_RADIO && ISA && (PCI || BROKEN)
@@ -355,6 +459,8 @@ config PRISM54
say M here and read <file:Documentation/modules.txt>. The module
will be called prism54.ko.
+source "drivers/net/wireless/hostap/Kconfig"
+
# yes, this works even when no drivers are selected
config NET_WIRELESS
bool
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 2b87841322cc..0953cc0cdee6 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -2,6 +2,10 @@
# Makefile for the Linux Wireless network device drivers.
#
+obj-$(CONFIG_IPW2100) += ipw2100.o
+
+obj-$(CONFIG_IPW2200) += ipw2200.o
+
obj-$(CONFIG_STRIP) += strip.o
obj-$(CONFIG_ARLAN) += arlan.o
@@ -28,6 +32,8 @@ obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o
obj-$(CONFIG_PRISM54) += prism54/
+obj-$(CONFIG_HOSTAP) += hostap/
+
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index df20adcd0730..6db1fb6461de 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -1040,7 +1040,7 @@ typedef struct {
u16 status;
} WifiCtlHdr;
-WifiCtlHdr wifictlhdr8023 = {
+static WifiCtlHdr wifictlhdr8023 = {
.ctlhdr = {
.ctl = HOST_DONT_RLSE,
}
@@ -1111,13 +1111,13 @@ static int airo_thread(void *data);
static void timer_func( struct net_device *dev );
static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
#ifdef WIRELESS_EXT
-struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
+static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
static void airo_read_wireless_stats (struct airo_info *local);
#endif /* WIRELESS_EXT */
#ifdef CISCO_EXT
static int readrids(struct net_device *dev, aironet_ioctl *comp);
static int writerids(struct net_device *dev, aironet_ioctl *comp);
-int flashcard(struct net_device *dev, aironet_ioctl *comp);
+static int flashcard(struct net_device *dev, aironet_ioctl *comp);
#endif /* CISCO_EXT */
#ifdef MICSUPPORT
static void micinit(struct airo_info *ai);
@@ -1226,6 +1226,12 @@ static int setup_proc_entry( struct net_device *dev,
static int takedown_proc_entry( struct net_device *dev,
struct airo_info *apriv );
+static int cmdreset(struct airo_info *ai);
+static int setflashmode (struct airo_info *ai);
+static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
+static int flashputbuf(struct airo_info *ai);
+static int flashrestart(struct airo_info *ai,struct net_device *dev);
+
#ifdef MICSUPPORT
/***********************************************************************
* MIC ROUTINES *
@@ -1234,10 +1240,11 @@ static int takedown_proc_entry( struct net_device *dev,
static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq);
static void MoveWindow(miccntx *context, u32 micSeq);
-void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *);
-void emmh32_init(emmh32_context *context);
-void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
-void emmh32_final(emmh32_context *context, u8 digest[4]);
+static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *);
+static void emmh32_init(emmh32_context *context);
+static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
+static void emmh32_final(emmh32_context *context, u8 digest[4]);
+static int flashpchar(struct airo_info *ai,int byte,int dwelltime);
/* micinit - Initialize mic seed */
@@ -1315,7 +1322,7 @@ static int micsetup(struct airo_info *ai) {
return SUCCESS;
}
-char micsnap[]= {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
+static char micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
/*===========================================================================
* Description: Mic a packet
@@ -1570,7 +1577,7 @@ static void MoveWindow(miccntx *context, u32 micSeq)
static unsigned char aes_counter[16];
/* expand the key to fill the MMH coefficient array */
-void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *tfm)
+static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *tfm)
{
/* take the keying material, expand if necessary, truncate at 16-bytes */
/* run through AES counter mode to generate context->coeff[] */
@@ -1602,7 +1609,7 @@ void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto
}
/* prepare for calculation of a new mic */
-void emmh32_init(emmh32_context *context)
+static void emmh32_init(emmh32_context *context)
{
/* prepare for new mic calculation */
context->accum = 0;
@@ -1610,7 +1617,7 @@ void emmh32_init(emmh32_context *context)
}
/* add some bytes to the mic calculation */
-void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
+static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
{
int coeff_position, byte_position;
@@ -1652,7 +1659,7 @@ void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
/* calculate the mic */
-void emmh32_final(emmh32_context *context, u8 digest[4])
+static void emmh32_final(emmh32_context *context, u8 digest[4])
{
int coeff_position, byte_position;
u32 val;
@@ -2255,7 +2262,7 @@ static void airo_read_stats(struct airo_info *ai) {
ai->stats.rx_fifo_errors = vals[0];
}
-struct net_device_stats *airo_get_stats(struct net_device *dev)
+static struct net_device_stats *airo_get_stats(struct net_device *dev)
{
struct airo_info *local = dev->priv;
@@ -2414,7 +2421,7 @@ EXPORT_SYMBOL(stop_airo_card);
static int add_airo_dev( struct net_device *dev );
-int wll_header_parse(struct sk_buff *skb, unsigned char *haddr)
+static int wll_header_parse(struct sk_buff *skb, unsigned char *haddr)
{
memcpy(haddr, skb->mac.raw + 10, ETH_ALEN);
return ETH_ALEN;
@@ -2681,7 +2688,7 @@ static struct net_device *init_wifidev(struct airo_info *ai,
return dev;
}
-int reset_card( struct net_device *dev , int lock) {
+static int reset_card( struct net_device *dev , int lock) {
struct airo_info *ai = dev->priv;
if (lock && down_interruptible(&ai->sem))
@@ -2696,9 +2703,9 @@ int reset_card( struct net_device *dev , int lock) {
return 0;
}
-struct net_device *_init_airo_card( unsigned short irq, int port,
- int is_pcmcia, struct pci_dev *pci,
- struct device *dmdev )
+static struct net_device *_init_airo_card( unsigned short irq, int port,
+ int is_pcmcia, struct pci_dev *pci,
+ struct device *dmdev )
{
struct net_device *dev;
struct airo_info *ai;
@@ -7235,7 +7242,7 @@ static void airo_read_wireless_stats(struct airo_info *local)
local->wstats.miss.beacon = vals[34];
}
-struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
+static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
{
struct airo_info *local = dev->priv;
@@ -7450,14 +7457,8 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) {
* Flash command switch table
*/
-int flashcard(struct net_device *dev, aironet_ioctl *comp) {
+static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
int z;
- int cmdreset(struct airo_info *);
- int setflashmode(struct airo_info *);
- int flashgchar(struct airo_info *,int,int);
- int flashpchar(struct airo_info *,int,int);
- int flashputbuf(struct airo_info *);
- int flashrestart(struct airo_info *,struct net_device *);
/* Only super-user can modify flash */
if (!capable(CAP_NET_ADMIN))
@@ -7515,7 +7516,7 @@ int flashcard(struct net_device *dev, aironet_ioctl *comp) {
* card.
*/
-int cmdreset(struct airo_info *ai) {
+static int cmdreset(struct airo_info *ai) {
disable_MAC(ai, 1);
if(!waitbusy (ai)){
@@ -7539,7 +7540,7 @@ int cmdreset(struct airo_info *ai) {
* mode
*/
-int setflashmode (struct airo_info *ai) {
+static int setflashmode (struct airo_info *ai) {
set_bit (FLAG_FLASHING, &ai->flags);
OUT4500(ai, SWS0, FLASH_COMMAND);
@@ -7566,7 +7567,7 @@ int setflashmode (struct airo_info *ai) {
* x 50us for echo .
*/
-int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
+static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
int echo;
int waittime;
@@ -7606,7 +7607,7 @@ int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
* Get a character from the card matching matchbyte
* Step 3)
*/
-int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
+static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
int rchar;
unsigned char rbyte=0;
@@ -7637,7 +7638,7 @@ int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
* send to the card
*/
-int flashputbuf(struct airo_info *ai){
+static int flashputbuf(struct airo_info *ai){
int nwords;
/* Write stuff */
@@ -7659,7 +7660,7 @@ int flashputbuf(struct airo_info *ai){
/*
*
*/
-int flashrestart(struct airo_info *ai,struct net_device *dev){
+static int flashrestart(struct airo_info *ai,struct net_device *dev){
int i,status;
ssleep(1); /* Added 12/7/00 */
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index 18a7d38d2a13..f48a6e729224 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -68,7 +68,7 @@
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/firmware.h>
-#include "ieee802_11.h"
+#include <net/ieee80211.h>
#include "atmel.h"
#define DRIVER_MAJOR 0
@@ -618,12 +618,12 @@ static int atmel_lock_mac(struct atmel_private *priv);
static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data);
static void atmel_command_irq(struct atmel_private *priv);
static int atmel_validate_channel(struct atmel_private *priv, int channel);
-static void atmel_management_frame(struct atmel_private *priv, struct ieee802_11_hdr *header,
+static void atmel_management_frame(struct atmel_private *priv, struct ieee80211_hdr *header,
u16 frame_len, u8 rssi);
static void atmel_management_timer(u_long a);
static void atmel_send_command(struct atmel_private *priv, int command, void *cmd, int cmd_size);
static int atmel_send_command_wait(struct atmel_private *priv, int command, void *cmd, int cmd_size);
-static void atmel_transmit_management_frame(struct atmel_private *priv, struct ieee802_11_hdr *header,
+static void atmel_transmit_management_frame(struct atmel_private *priv, struct ieee80211_hdr *header,
u8 *body, int body_len);
static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index);
@@ -827,7 +827,7 @@ static void tx_update_descriptor(struct atmel_private *priv, int is_bcast, u16 l
static int start_tx (struct sk_buff *skb, struct net_device *dev)
{
struct atmel_private *priv = netdev_priv(dev);
- struct ieee802_11_hdr header;
+ struct ieee80211_hdr header;
unsigned long flags;
u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
u8 SNAP_RFC1024[6] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
@@ -863,17 +863,17 @@ static int start_tx (struct sk_buff *skb, struct net_device *dev)
return 1;
}
- frame_ctl = IEEE802_11_FTYPE_DATA;
+ frame_ctl = IEEE80211_FTYPE_DATA;
header.duration_id = 0;
header.seq_ctl = 0;
if (priv->wep_is_on)
- frame_ctl |= IEEE802_11_FCTL_WEP;
+ frame_ctl |= IEEE80211_FCTL_PROTECTED;
if (priv->operating_mode == IW_MODE_ADHOC) {
memcpy(&header.addr1, skb->data, 6);
memcpy(&header.addr2, dev->dev_addr, 6);
memcpy(&header.addr3, priv->BSSID, 6);
} else {
- frame_ctl |= IEEE802_11_FCTL_TODS;
+ frame_ctl |= IEEE80211_FCTL_TODS;
memcpy(&header.addr1, priv->CurrentBSSID, 6);
memcpy(&header.addr2, dev->dev_addr, 6);
memcpy(&header.addr3, skb->data, 6);
@@ -902,7 +902,7 @@ static int start_tx (struct sk_buff *skb, struct net_device *dev)
}
static void atmel_transmit_management_frame(struct atmel_private *priv,
- struct ieee802_11_hdr *header,
+ struct ieee80211_hdr *header,
u8 *body, int body_len)
{
u16 buff;
@@ -917,7 +917,7 @@ static void atmel_transmit_management_frame(struct atmel_private *priv,
tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT);
}
-static void fast_rx_path(struct atmel_private *priv, struct ieee802_11_hdr *header,
+static void fast_rx_path(struct atmel_private *priv, struct ieee80211_hdr *header,
u16 msdu_size, u16 rx_packet_loc, u32 crc)
{
/* fast path: unfragmented packet copy directly into skbuf */
@@ -955,7 +955,7 @@ static void fast_rx_path(struct atmel_private *priv, struct ieee802_11_hdr *head
}
memcpy(skbp, header->addr1, 6); /* destination address */
- if (le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_FROMDS)
+ if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
memcpy(&skbp[6], header->addr3, 6);
else
memcpy(&skbp[6], header->addr2, 6); /* source address */
@@ -990,14 +990,14 @@ static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size)
return (crc ^ 0xffffffff) == netcrc;
}
-static void frag_rx_path(struct atmel_private *priv, struct ieee802_11_hdr *header,
+static void frag_rx_path(struct atmel_private *priv, struct ieee80211_hdr *header,
u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, u8 frag_no, int more_frags)
{
u8 mac4[6];
u8 source[6];
struct sk_buff *skb;
- if (le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_FROMDS)
+ if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
memcpy(source, header->addr3, 6);
else
memcpy(source, header->addr2, 6);
@@ -1082,7 +1082,7 @@ static void frag_rx_path(struct atmel_private *priv, struct ieee802_11_hdr *head
static void rx_done_irq(struct atmel_private *priv)
{
int i;
- struct ieee802_11_hdr header;
+ struct ieee80211_hdr header;
for (i = 0;
atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID &&
@@ -1117,7 +1117,7 @@ static void rx_done_irq(struct atmel_private *priv)
/* probe for CRC use here if needed once five packets have arrived with
the same crc status, we assume we know what's happening and stop probing */
if (priv->probe_crc) {
- if (!priv->wep_is_on || !(frame_ctl & IEEE802_11_FCTL_WEP)) {
+ if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) {
priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size);
} else {
priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24);
@@ -1132,16 +1132,16 @@ static void rx_done_irq(struct atmel_private *priv)
}
/* don't CRC header when WEP in use */
- if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE802_11_FCTL_WEP))) {
+ if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) {
crc = crc32_le(0xffffffff, (unsigned char *)&header, 24);
}
msdu_size -= 24; /* header */
- if ((frame_ctl & IEEE802_11_FCTL_FTYPE) == IEEE802_11_FTYPE_DATA) {
+ if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
- int more_fragments = frame_ctl & IEEE802_11_FCTL_MOREFRAGS;
- u8 packet_fragment_no = seq_control & IEEE802_11_SCTL_FRAG;
- u16 packet_sequence_no = (seq_control & IEEE802_11_SCTL_SEQ) >> 4;
+ int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS;
+ u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG;
+ u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4;
if (!more_fragments && packet_fragment_no == 0 ) {
fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc);
@@ -1151,7 +1151,7 @@ static void rx_done_irq(struct atmel_private *priv)
}
}
- if ((frame_ctl & IEEE802_11_FCTL_FTYPE) == IEEE802_11_FTYPE_MGMT) {
+ if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
/* copy rest of packet into buffer */
atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size);
@@ -2663,10 +2663,10 @@ static void handle_beacon_probe(struct atmel_private *priv, u16 capability, u8 c
static void send_authentication_request(struct atmel_private *priv, u8 *challenge, int challenge_len)
{
- struct ieee802_11_hdr header;
+ struct ieee80211_hdr header;
struct auth_body auth;
- header.frame_ctl = cpu_to_le16(IEEE802_11_FTYPE_MGMT | IEEE802_11_STYPE_AUTH);
+ header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
header.duration_id = cpu_to_le16(0x8000);
header.seq_ctl = 0;
memcpy(header.addr1, priv->CurrentBSSID, 6);
@@ -2677,7 +2677,7 @@ static void send_authentication_request(struct atmel_private *priv, u8 *challeng
auth.alg = cpu_to_le16(C80211_MGMT_AAN_SHAREDKEY);
/* no WEP for authentication frames with TrSeqNo 1 */
if (priv->CurrentAuthentTransactionSeqNum != 1)
- header.frame_ctl |= cpu_to_le16(IEEE802_11_FCTL_WEP);
+ header.frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
} else {
auth.alg = cpu_to_le16(C80211_MGMT_AAN_OPENSYSTEM);
}
@@ -2701,7 +2701,7 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
{
u8 *ssid_el_p;
int bodysize;
- struct ieee802_11_hdr header;
+ struct ieee80211_hdr header;
struct ass_req_format {
u16 capability;
u16 listen_interval;
@@ -2714,8 +2714,8 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
u8 rates[4];
} body;
- header.frame_ctl = cpu_to_le16(IEEE802_11_FTYPE_MGMT |
- (is_reassoc ? IEEE802_11_STYPE_REASSOC_REQ : IEEE802_11_STYPE_ASSOC_REQ));
+ header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ (is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ));
header.duration_id = cpu_to_le16(0x8000);
header.seq_ctl = 0;
@@ -2751,9 +2751,9 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize);
}
-static int is_frame_from_current_bss(struct atmel_private *priv, struct ieee802_11_hdr *header)
+static int is_frame_from_current_bss(struct atmel_private *priv, struct ieee80211_hdr *header)
{
- if (le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_FROMDS)
+ if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0;
else
return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0;
@@ -2801,7 +2801,7 @@ static int retrieve_bss(struct atmel_private *priv)
}
-static void store_bss_info(struct atmel_private *priv, struct ieee802_11_hdr *header,
+static void store_bss_info(struct atmel_private *priv, struct ieee80211_hdr *header,
u16 capability, u16 beacon_period, u8 channel, u8 rssi,
u8 ssid_len, u8 *ssid, int is_beacon)
{
@@ -3085,12 +3085,12 @@ static void atmel_smooth_qual(struct atmel_private *priv)
}
/* deals with incoming managment frames. */
-static void atmel_management_frame(struct atmel_private *priv, struct ieee802_11_hdr *header,
+static void atmel_management_frame(struct atmel_private *priv, struct ieee80211_hdr *header,
u16 frame_len, u8 rssi)
{
u16 subtype;
- switch (subtype = le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_STYPE) {
+ switch (subtype = le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_STYPE) {
case C80211_SUBTYPE_MGMT_BEACON :
case C80211_SUBTYPE_MGMT_ProbeResponse:
diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig
new file mode 100644
index 000000000000..56f41c714d38
--- /dev/null
+++ b/drivers/net/wireless/hostap/Kconfig
@@ -0,0 +1,73 @@
+config HOSTAP
+ tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)"
+ depends on NET_RADIO
+ select IEEE80211
+ select IEEE80211_CRYPT_WEP
+ ---help---
+ Shared driver code for IEEE 802.11b wireless cards based on
+ Intersil Prism2/2.5/3 chipset. This driver supports so called
+ Host AP mode that allows the card to act as an IEEE 802.11
+ access point.
+
+ See <http://hostap.epitest.fi/> for more information about the
+ Host AP driver configuration and tools. This site includes
+ information and tools (hostapd and wpa_supplicant) for WPA/WPA2
+ support.
+
+ This option includes the base Host AP driver code that is shared by
+ different hardware models. You will also need to enable support for
+ PLX/PCI/CS version of the driver to actually use the driver.
+
+ The driver can be compiled as a module and it will be called
+ "hostap.ko".
+
+config HOSTAP_FIRMWARE
+ bool "Support downloading firmware images with Host AP driver"
+ depends on HOSTAP
+ ---help---
+ Configure Host AP driver to include support for firmware image
+ download. Current version supports only downloading to volatile, i.e.,
+ RAM memory. Flash upgrade is not yet supported.
+
+ Firmware image downloading needs user space tool, prism2_srec. It is
+ available from http://hostap.epitest.fi/.
+
+config HOSTAP_PLX
+ tristate "Host AP driver for Prism2/2.5/3 in PLX9052 PCI adaptors"
+ depends on PCI && HOSTAP
+ ---help---
+ Host AP driver's version for Prism2/2.5/3 PC Cards in PLX9052 based
+ PCI adaptors.
+
+ "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
+ driver and its help text includes more information about the Host AP
+ driver.
+
+ The driver can be compiled as a module and will be named
+ "hostap_plx.ko".
+
+config HOSTAP_PCI
+ tristate "Host AP driver for Prism2.5 PCI adaptors"
+ depends on PCI && HOSTAP
+ ---help---
+ Host AP driver's version for Prism2.5 PCI adaptors.
+
+ "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
+ driver and its help text includes more information about the Host AP
+ driver.
+
+ The driver can be compiled as a module and will be named
+ "hostap_pci.ko".
+
+config HOSTAP_CS
+ tristate "Host AP driver for Prism2/2.5/3 PC Cards"
+ depends on PCMCIA!=n && HOSTAP
+ ---help---
+ Host AP driver's version for Prism2/2.5/3 PC Cards.
+
+ "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
+ driver and its help text includes more information about the Host AP
+ driver.
+
+ The driver can be compiled as a module and will be named
+ "hostap_cs.ko".
diff --git a/drivers/net/wireless/hostap/Makefile b/drivers/net/wireless/hostap/Makefile
new file mode 100644
index 000000000000..fc62235bfc24
--- /dev/null
+++ b/drivers/net/wireless/hostap/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_HOSTAP) += hostap.o
+
+obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o
+obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o
+obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o
diff --git a/drivers/net/wireless/hostap/hostap.c b/drivers/net/wireless/hostap/hostap.c
new file mode 100644
index 000000000000..e7f5821b4942
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap.c
@@ -0,0 +1,1198 @@
+/*
+ * Host AP (software wireless LAN access point) driver for
+ * Intersil Prism2/2.5/3 - hostap.o module, common routines
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/workqueue.h>
+#include <linux/kmod.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+#include <net/ieee80211_crypt.h>
+#include <asm/uaccess.h>
+
+#include "hostap_wlan.h"
+#include "hostap_80211.h"
+#include "hostap_ap.h"
+#include "hostap.h"
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Host AP common routines");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PRISM2_VERSION);
+
+#define TX_TIMEOUT (2 * HZ)
+
+#define PRISM2_MAX_FRAME_SIZE 2304
+#define PRISM2_MIN_MTU 256
+/* FIX: */
+#define PRISM2_MAX_MTU (PRISM2_MAX_FRAME_SIZE - (6 /* LLC */ + 8 /* WEP */))
+
+
+/* hostap.c */
+static int prism2_wds_add(local_info_t *local, u8 *remote_addr,
+ int rtnl_locked);
+static int prism2_wds_del(local_info_t *local, u8 *remote_addr,
+ int rtnl_locked, int do_not_remove);
+
+/* hostap_ap.c */
+static int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
+ struct iw_quality qual[], int buf_size,
+ int aplist);
+static int prism2_ap_translate_scan(struct net_device *dev, char *buffer);
+static int prism2_hostapd(struct ap_data *ap,
+ struct prism2_hostapd_param *param);
+static void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
+ struct ieee80211_crypt_data ***crypt);
+static void ap_control_kickall(struct ap_data *ap);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+static int ap_control_add_mac(struct mac_restrictions *mac_restrictions,
+ u8 *mac);
+static int ap_control_del_mac(struct mac_restrictions *mac_restrictions,
+ u8 *mac);
+static void ap_control_flush_macs(struct mac_restrictions *mac_restrictions);
+static int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev,
+ u8 *mac);
+#endif /* !PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+static const long freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+ 2447, 2452, 2457, 2462, 2467, 2472, 2484 };
+#define FREQ_COUNT (sizeof(freq_list) / sizeof(freq_list[0]))
+
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+static unsigned char rfc1042_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static unsigned char bridge_tunnel_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+/* No encapsulation header if EtherType < 0x600 (=length) */
+
+
+/* FIX: these could be compiled separately and linked together to hostap.o */
+#include "hostap_ap.c"
+#include "hostap_info.c"
+#include "hostap_ioctl.c"
+#include "hostap_proc.c"
+#include "hostap_80211_rx.c"
+#include "hostap_80211_tx.c"
+
+
+struct net_device * hostap_add_interface(struct local_info *local,
+ int type, int rtnl_locked,
+ const char *prefix,
+ const char *name)
+{
+ struct net_device *dev, *mdev;
+ struct hostap_interface *iface;
+ int ret;
+
+ dev = alloc_etherdev(sizeof(struct hostap_interface));
+ if (dev == NULL)
+ return NULL;
+
+ iface = netdev_priv(dev);
+ iface->dev = dev;
+ iface->local = local;
+ iface->type = type;
+ list_add(&iface->list, &local->hostap_interfaces);
+
+ mdev = local->dev;
+ memcpy(dev->dev_addr, mdev->dev_addr, ETH_ALEN);
+ dev->base_addr = mdev->base_addr;
+ dev->irq = mdev->irq;
+ dev->mem_start = mdev->mem_start;
+ dev->mem_end = mdev->mem_end;
+
+ hostap_setup_dev(dev, local, 0);
+ dev->destructor = free_netdev;
+
+ sprintf(dev->name, "%s%s", prefix, name);
+ if (!rtnl_locked)
+ rtnl_lock();
+
+ ret = 0;
+ if (strchr(dev->name, '%'))
+ ret = dev_alloc_name(dev, dev->name);
+
+ SET_NETDEV_DEV(dev, mdev->class_dev.dev);
+ if (ret >= 0)
+ ret = register_netdevice(dev);
+
+ if (!rtnl_locked)
+ rtnl_unlock();
+
+ if (ret < 0) {
+ printk(KERN_WARNING "%s: failed to add new netdevice!\n",
+ dev->name);
+ free_netdev(dev);
+ return NULL;
+ }
+
+ printk(KERN_DEBUG "%s: registered netdevice %s\n",
+ mdev->name, dev->name);
+
+ return dev;
+}
+
+
+void hostap_remove_interface(struct net_device *dev, int rtnl_locked,
+ int remove_from_list)
+{
+ struct hostap_interface *iface;
+
+ if (!dev)
+ return;
+
+ iface = netdev_priv(dev);
+
+ if (remove_from_list) {
+ list_del(&iface->list);
+ }
+
+ if (dev == iface->local->ddev)
+ iface->local->ddev = NULL;
+ else if (dev == iface->local->apdev)
+ iface->local->apdev = NULL;
+ else if (dev == iface->local->stadev)
+ iface->local->stadev = NULL;
+
+ if (rtnl_locked)
+ unregister_netdevice(dev);
+ else
+ unregister_netdev(dev);
+
+ /* dev->destructor = free_netdev() will free the device data, including
+ * private data, when removing the device */
+}
+
+
+static inline int prism2_wds_special_addr(u8 *addr)
+{
+ if (addr[0] || addr[1] || addr[2] || addr[3] || addr[4] || addr[5])
+ return 0;
+
+ return 1;
+}
+
+
+static int prism2_wds_add(local_info_t *local, u8 *remote_addr,
+ int rtnl_locked)
+{
+ struct net_device *dev;
+ struct list_head *ptr;
+ struct hostap_interface *iface, *empty, *match;
+
+ empty = match = NULL;
+ read_lock_bh(&local->iface_lock);
+ list_for_each(ptr, &local->hostap_interfaces) {
+ iface = list_entry(ptr, struct hostap_interface, list);
+ if (iface->type != HOSTAP_INTERFACE_WDS)
+ continue;
+
+ if (prism2_wds_special_addr(iface->u.wds.remote_addr))
+ empty = iface;
+ else if (memcmp(iface->u.wds.remote_addr, remote_addr,
+ ETH_ALEN) == 0) {
+ match = iface;
+ break;
+ }
+ }
+ if (!match && empty && !prism2_wds_special_addr(remote_addr)) {
+ /* take pre-allocated entry into use */
+ memcpy(empty->u.wds.remote_addr, remote_addr, ETH_ALEN);
+ read_unlock_bh(&local->iface_lock);
+ printk(KERN_DEBUG "%s: using pre-allocated WDS netdevice %s\n",
+ local->dev->name, empty->dev->name);
+ return 0;
+ }
+ read_unlock_bh(&local->iface_lock);
+
+ if (!prism2_wds_special_addr(remote_addr)) {
+ if (match)
+ return -EEXIST;
+ hostap_add_sta(local->ap, remote_addr);
+ }
+
+ if (local->wds_connections >= local->wds_max_connections)
+ return -ENOBUFS;
+
+ /* verify that there is room for wds# postfix in the interface name */
+ if (strlen(local->dev->name) > IFNAMSIZ - 5) {
+ printk(KERN_DEBUG "'%s' too long base device name\n",
+ local->dev->name);
+ return -EINVAL;
+ }
+
+ dev = hostap_add_interface(local, HOSTAP_INTERFACE_WDS, rtnl_locked,
+ local->ddev->name, "wds%d");
+ if (dev == NULL)
+ return -ENOMEM;
+
+ iface = netdev_priv(dev);
+ memcpy(iface->u.wds.remote_addr, remote_addr, ETH_ALEN);
+
+ local->wds_connections++;
+
+ return 0;
+}
+
+
+static int prism2_wds_del(local_info_t *local, u8 *remote_addr,
+ int rtnl_locked, int do_not_remove)
+{
+ unsigned long flags;
+ struct list_head *ptr;
+ struct hostap_interface *iface, *selected = NULL;
+
+ write_lock_irqsave(&local->iface_lock, flags);
+ list_for_each(ptr, &local->hostap_interfaces) {
+ iface = list_entry(ptr, struct hostap_interface, list);
+ if (iface->type != HOSTAP_INTERFACE_WDS)
+ continue;
+
+ if (memcmp(iface->u.wds.remote_addr, remote_addr,
+ ETH_ALEN) == 0) {
+ selected = iface;
+ break;
+ }
+ }
+ if (selected && !do_not_remove)
+ list_del(&selected->list);
+ write_unlock_irqrestore(&local->iface_lock, flags);
+
+ if (selected) {
+ if (do_not_remove)
+ memset(selected->u.wds.remote_addr, 0, ETH_ALEN);
+ else {
+ hostap_remove_interface(selected->dev, rtnl_locked, 0);
+ local->wds_connections--;
+ }
+ }
+
+ return selected ? 0 : -ENODEV;
+}
+
+
+u16 hostap_tx_callback_register(local_info_t *local,
+ void (*func)(struct sk_buff *, int ok, void *),
+ void *data)
+{
+ unsigned long flags;
+ struct hostap_tx_callback_info *entry;
+
+ entry = (struct hostap_tx_callback_info *) kmalloc(sizeof(*entry),
+ GFP_ATOMIC);
+ if (entry == NULL)
+ return 0;
+
+ entry->func = func;
+ entry->data = data;
+
+ spin_lock_irqsave(&local->lock, flags);
+ entry->idx = local->tx_callback ? local->tx_callback->idx + 1 : 1;
+ entry->next = local->tx_callback;
+ local->tx_callback = entry;
+ spin_unlock_irqrestore(&local->lock, flags);
+
+ return entry->idx;
+}
+
+
+int hostap_tx_callback_unregister(local_info_t *local, u16 idx)
+{
+ unsigned long flags;
+ struct hostap_tx_callback_info *cb, *prev = NULL;
+
+ spin_lock_irqsave(&local->lock, flags);
+ cb = local->tx_callback;
+ while (cb != NULL && cb->idx != idx) {
+ prev = cb;
+ cb = cb->next;
+ }
+ if (cb) {
+ if (prev == NULL)
+ local->tx_callback = cb->next;
+ else
+ prev->next = cb->next;
+ kfree(cb);
+ }
+ spin_unlock_irqrestore(&local->lock, flags);
+
+ return cb ? 0 : -1;
+}
+
+
+/* val is in host byte order */
+int hostap_set_word(struct net_device *dev, int rid, u16 val)
+{
+ struct hostap_interface *iface;
+ u16 tmp = cpu_to_le16(val);
+ iface = netdev_priv(dev);
+ return iface->local->func->set_rid(dev, rid, &tmp, 2);
+}
+
+
+int hostap_set_string(struct net_device *dev, int rid, const char *val)
+{
+ struct hostap_interface *iface;
+ char buf[MAX_SSID_LEN + 2];
+ int len;
+
+ iface = netdev_priv(dev);
+ len = strlen(val);
+ if (len > MAX_SSID_LEN)
+ return -1;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = len; /* little endian 16 bit word */
+ memcpy(buf + 2, val, len);
+
+ return iface->local->func->set_rid(dev, rid, &buf, MAX_SSID_LEN + 2);
+}
+
+
+u16 hostap_get_porttype(local_info_t *local)
+{
+ if (local->iw_mode == IW_MODE_ADHOC && local->pseudo_adhoc)
+ return HFA384X_PORTTYPE_PSEUDO_IBSS;
+ if (local->iw_mode == IW_MODE_ADHOC)
+ return HFA384X_PORTTYPE_IBSS;
+ if (local->iw_mode == IW_MODE_INFRA)
+ return HFA384X_PORTTYPE_BSS;
+ if (local->iw_mode == IW_MODE_REPEAT)
+ return HFA384X_PORTTYPE_WDS;
+ if (local->iw_mode == IW_MODE_MONITOR)
+ return HFA384X_PORTTYPE_PSEUDO_IBSS;
+ return HFA384X_PORTTYPE_HOSTAP;
+}
+
+
+int hostap_set_encryption(local_info_t *local)
+{
+ u16 val, old_val;
+ int i, keylen, len, idx;
+ char keybuf[WEP_KEY_LEN + 1];
+ enum { NONE, WEP, OTHER } encrypt_type;
+
+ idx = local->tx_keyidx;
+ if (local->crypt[idx] == NULL || local->crypt[idx]->ops == NULL)
+ encrypt_type = NONE;
+ else if (strcmp(local->crypt[idx]->ops->name, "WEP") == 0)
+ encrypt_type = WEP;
+ else
+ encrypt_type = OTHER;
+
+ if (local->func->get_rid(local->dev, HFA384X_RID_CNFWEPFLAGS, &val, 2,
+ 1) < 0) {
+ printk(KERN_DEBUG "Could not read current WEP flags.\n");
+ goto fail;
+ }
+ le16_to_cpus(&val);
+ old_val = val;
+
+ if (encrypt_type != NONE || local->privacy_invoked)
+ val |= HFA384X_WEPFLAGS_PRIVACYINVOKED;
+ else
+ val &= ~HFA384X_WEPFLAGS_PRIVACYINVOKED;
+
+ if (local->open_wep || encrypt_type == NONE ||
+ ((local->ieee_802_1x || local->wpa) && local->host_decrypt))
+ val &= ~HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED;
+ else
+ val |= HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED;
+
+ if ((encrypt_type != NONE || local->privacy_invoked) &&
+ (encrypt_type == OTHER || local->host_encrypt))
+ val |= HFA384X_WEPFLAGS_HOSTENCRYPT;
+ else
+ val &= ~HFA384X_WEPFLAGS_HOSTENCRYPT;
+ if ((encrypt_type != NONE || local->privacy_invoked) &&
+ (encrypt_type == OTHER || local->host_decrypt))
+ val |= HFA384X_WEPFLAGS_HOSTDECRYPT;
+ else
+ val &= ~HFA384X_WEPFLAGS_HOSTDECRYPT;
+
+ if (val != old_val &&
+ hostap_set_word(local->dev, HFA384X_RID_CNFWEPFLAGS, val)) {
+ printk(KERN_DEBUG "Could not write new WEP flags (0x%x)\n",
+ val);
+ goto fail;
+ }
+
+ if (encrypt_type != WEP)
+ return 0;
+
+ /* 104-bit support seems to require that all the keys are set to the
+ * same keylen */
+ keylen = 6; /* first 5 octets */
+ len = local->crypt[idx]->ops->get_key(keybuf, sizeof(keybuf),
+ NULL, local->crypt[idx]->priv);
+ if (idx >= 0 && idx < WEP_KEYS && len > 5)
+ keylen = WEP_KEY_LEN + 1; /* first 13 octets */
+
+ for (i = 0; i < WEP_KEYS; i++) {
+ memset(keybuf, 0, sizeof(keybuf));
+ if (local->crypt[i]) {
+ (void) local->crypt[i]->ops->get_key(
+ keybuf, sizeof(keybuf),
+ NULL, local->crypt[i]->priv);
+ }
+ if (local->func->set_rid(local->dev,
+ HFA384X_RID_CNFDEFAULTKEY0 + i,
+ keybuf, keylen)) {
+ printk(KERN_DEBUG "Could not set key %d (len=%d)\n",
+ i, keylen);
+ goto fail;
+ }
+ }
+ if (hostap_set_word(local->dev, HFA384X_RID_CNFWEPDEFAULTKEYID, idx)) {
+ printk(KERN_DEBUG "Could not set default keyid %d\n", idx);
+ goto fail;
+ }
+
+ return 0;
+
+ fail:
+ printk(KERN_DEBUG "%s: encryption setup failed\n", local->dev->name);
+ return -1;
+}
+
+
+int hostap_set_antsel(local_info_t *local)
+{
+ u16 val;
+ int ret = 0;
+
+ if (local->antsel_tx != HOSTAP_ANTSEL_DO_NOT_TOUCH &&
+ local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF,
+ HFA386X_CR_TX_CONFIGURE,
+ NULL, &val) == 0) {
+ val &= ~(BIT(2) | BIT(1));
+ switch (local->antsel_tx) {
+ case HOSTAP_ANTSEL_DIVERSITY:
+ val |= BIT(1);
+ break;
+ case HOSTAP_ANTSEL_LOW:
+ break;
+ case HOSTAP_ANTSEL_HIGH:
+ val |= BIT(2);
+ break;
+ }
+
+ if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF,
+ HFA386X_CR_TX_CONFIGURE, &val, NULL)) {
+ printk(KERN_INFO "%s: setting TX AntSel failed\n",
+ local->dev->name);
+ ret = -1;
+ }
+ }
+
+ if (local->antsel_rx != HOSTAP_ANTSEL_DO_NOT_TOUCH &&
+ local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF,
+ HFA386X_CR_RX_CONFIGURE,
+ NULL, &val) == 0) {
+ val &= ~(BIT(1) | BIT(0));
+ switch (local->antsel_rx) {
+ case HOSTAP_ANTSEL_DIVERSITY:
+ break;
+ case HOSTAP_ANTSEL_LOW:
+ val |= BIT(0);
+ break;
+ case HOSTAP_ANTSEL_HIGH:
+ val |= BIT(0) | BIT(1);
+ break;
+ }
+
+ if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF,
+ HFA386X_CR_RX_CONFIGURE, &val, NULL)) {
+ printk(KERN_INFO "%s: setting RX AntSel failed\n",
+ local->dev->name);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+
+int hostap_set_roaming(local_info_t *local)
+{
+ u16 val;
+
+ switch (local->host_roaming) {
+ case 1:
+ val = HFA384X_ROAMING_HOST;
+ break;
+ case 2:
+ val = HFA384X_ROAMING_DISABLED;
+ break;
+ case 0:
+ default:
+ val = HFA384X_ROAMING_FIRMWARE;
+ break;
+ }
+
+ return hostap_set_word(local->dev, HFA384X_RID_CNFROAMINGMODE, val);
+}
+
+
+int hostap_set_auth_algs(local_info_t *local)
+{
+ int val = local->auth_algs;
+ /* At least STA f/w v0.6.2 seems to have issues with cnfAuthentication
+ * set to include both Open and Shared Key flags. It tries to use
+ * Shared Key authentication in that case even if WEP keys are not
+ * configured.. STA f/w v0.7.6 is able to handle such configuration,
+ * but it is unknown when this was fixed between 0.6.2 .. 0.7.6. */
+ if (local->sta_fw_ver < PRISM2_FW_VER(0,7,0) &&
+ val != PRISM2_AUTH_OPEN && val != PRISM2_AUTH_SHARED_KEY)
+ val = PRISM2_AUTH_OPEN;
+
+ if (hostap_set_word(local->dev, HFA384X_RID_CNFAUTHENTICATION, val)) {
+ printk(KERN_INFO "%s: cnfAuthentication setting to 0x%x "
+ "failed\n", local->dev->name, local->auth_algs);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+void hostap_dump_rx_header(const char *name, const struct hfa384x_rx_frame *rx)
+{
+ u16 status, fc;
+
+ status = __le16_to_cpu(rx->status);
+
+ printk(KERN_DEBUG "%s: RX status=0x%04x (port=%d, type=%d, "
+ "fcserr=%d) silence=%d signal=%d rate=%d rxflow=%d; "
+ "jiffies=%ld\n",
+ name, status, (status >> 8) & 0x07, status >> 13, status & 1,
+ rx->silence, rx->signal, rx->rate, rx->rxflow, jiffies);
+
+ fc = __le16_to_cpu(rx->frame_control);
+ printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x "
+ "data_len=%d%s%s\n",
+ fc, WLAN_FC_GET_TYPE(fc) >> 2, WLAN_FC_GET_STYPE(fc) >> 4,
+ __le16_to_cpu(rx->duration_id), __le16_to_cpu(rx->seq_ctrl),
+ __le16_to_cpu(rx->data_len),
+ fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "",
+ fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : "");
+
+ printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR " A4="
+ MACSTR "\n",
+ MAC2STR(rx->addr1), MAC2STR(rx->addr2), MAC2STR(rx->addr3),
+ MAC2STR(rx->addr4));
+
+ printk(KERN_DEBUG " dst=" MACSTR " src=" MACSTR " len=%d\n",
+ MAC2STR(rx->dst_addr), MAC2STR(rx->src_addr),
+ __be16_to_cpu(rx->len));
+}
+
+
+void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx)
+{
+ u16 fc;
+
+ printk(KERN_DEBUG "%s: TX status=0x%04x retry_count=%d tx_rate=%d "
+ "tx_control=0x%04x; jiffies=%ld\n",
+ name, __le16_to_cpu(tx->status), tx->retry_count, tx->tx_rate,
+ __le16_to_cpu(tx->tx_control), jiffies);
+
+ fc = __le16_to_cpu(tx->frame_control);
+ printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x "
+ "data_len=%d%s%s\n",
+ fc, WLAN_FC_GET_TYPE(fc) >> 2, WLAN_FC_GET_STYPE(fc) >> 4,
+ __le16_to_cpu(tx->duration_id), __le16_to_cpu(tx->seq_ctrl),
+ __le16_to_cpu(tx->data_len),
+ fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "",
+ fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : "");
+
+ printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR " A4="
+ MACSTR "\n",
+ MAC2STR(tx->addr1), MAC2STR(tx->addr2), MAC2STR(tx->addr3),
+ MAC2STR(tx->addr4));
+
+ printk(KERN_DEBUG " dst=" MACSTR " src=" MACSTR " len=%d\n",
+ MAC2STR(tx->dst_addr), MAC2STR(tx->src_addr),
+ __be16_to_cpu(tx->len));
+}
+
+
+int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr)
+{
+ memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); /* addr2 */
+ return ETH_ALEN;
+}
+
+
+int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr)
+{
+ if (*(u32 *)skb->mac.raw == LWNG_CAP_DID_BASE) {
+ memcpy(haddr, skb->mac.raw +
+ sizeof(struct linux_wlan_ng_prism_hdr) + 10,
+ ETH_ALEN); /* addr2 */
+ } else { /* (*(u32 *)skb->mac.raw == htonl(LWNG_CAPHDR_VERSION)) */
+ memcpy(haddr, skb->mac.raw +
+ sizeof(struct linux_wlan_ng_cap_hdr) + 10,
+ ETH_ALEN); /* addr2 */
+ }
+ return ETH_ALEN;
+}
+
+
+int hostap_80211_get_hdrlen(u16 fc)
+{
+ int hdrlen = 24;
+
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case IEEE80211_FTYPE_DATA:
+ if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
+ hdrlen = 30; /* Addr4 */
+ break;
+ case IEEE80211_FTYPE_CTL:
+ switch (WLAN_FC_GET_STYPE(fc)) {
+ case IEEE80211_STYPE_CTS:
+ case IEEE80211_STYPE_ACK:
+ hdrlen = 10;
+ break;
+ default:
+ hdrlen = 16;
+ break;
+ }
+ break;
+ }
+
+ return hdrlen;
+}
+
+
+struct net_device_stats *hostap_get_stats(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ iface = netdev_priv(dev);
+ return &iface->stats;
+}
+
+
+static int prism2_close(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ PDEBUG(DEBUG_FLOW, "%s: prism2_close\n", dev->name);
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (dev == local->ddev) {
+ prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING);
+ }
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ if (!local->hostapd && dev == local->dev &&
+ (!local->func->card_present || local->func->card_present(local)) &&
+ local->hw_ready && local->ap && local->iw_mode == IW_MODE_MASTER)
+ hostap_deauth_all_stas(dev, local->ap, 1);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ if (local->func->dev_close && local->func->dev_close(local))
+ return 0;
+
+ if (dev == local->dev) {
+ local->func->hw_shutdown(dev, HOSTAP_HW_ENABLE_CMDCOMPL);
+ }
+
+ if (netif_running(dev)) {
+ netif_stop_queue(dev);
+ netif_device_detach(dev);
+ }
+
+ flush_scheduled_work();
+
+ module_put(local->hw_module);
+
+ local->num_dev_open--;
+
+ if (dev != local->dev && local->dev->flags & IFF_UP &&
+ local->master_dev_auto_open && local->num_dev_open == 1) {
+ /* Close master radio interface automatically if it was also
+ * opened automatically and we are now closing the last
+ * remaining non-master device. */
+ dev_close(local->dev);
+ }
+
+ return 0;
+}
+
+
+static int prism2_open(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ PDEBUG(DEBUG_FLOW, "%s: prism2_open\n", dev->name);
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->no_pri) {
+ printk(KERN_DEBUG "%s: could not set interface UP - no PRI "
+ "f/w\n", dev->name);
+ return 1;
+ }
+
+ if ((local->func->card_present && !local->func->card_present(local)) ||
+ local->hw_downloading)
+ return -ENODEV;
+
+ if (local->func->dev_open && local->func->dev_open(local))
+ return 1;
+
+ if (!try_module_get(local->hw_module))
+ return -ENODEV;
+ local->num_dev_open++;
+
+ if (!local->dev_enabled && local->func->hw_enable(dev, 1)) {
+ printk(KERN_WARNING "%s: could not enable MAC port\n",
+ dev->name);
+ prism2_close(dev);
+ return 1;
+ }
+ if (!local->dev_enabled)
+ prism2_callback(local, PRISM2_CALLBACK_ENABLE);
+ local->dev_enabled = 1;
+
+ if (dev != local->dev && !(local->dev->flags & IFF_UP)) {
+ /* Master radio interface is needed for all operation, so open
+ * it automatically when any virtual net_device is opened. */
+ local->master_dev_auto_open = 1;
+ dev_open(local->dev);
+ }
+
+ netif_device_attach(dev);
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+
+static int prism2_set_mac_address(struct net_device *dev, void *p)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct list_head *ptr;
+ struct sockaddr *addr = p;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->func->set_rid(dev, HFA384X_RID_CNFOWNMACADDR, addr->sa_data,
+ ETH_ALEN) < 0 || local->func->reset_port(dev))
+ return -EINVAL;
+
+ read_lock_bh(&local->iface_lock);
+ list_for_each(ptr, &local->hostap_interfaces) {
+ iface = list_entry(ptr, struct hostap_interface, list);
+ memcpy(iface->dev->dev_addr, addr->sa_data, ETH_ALEN);
+ }
+ memcpy(local->dev->dev_addr, addr->sa_data, ETH_ALEN);
+ read_unlock_bh(&local->iface_lock);
+
+ return 0;
+}
+
+
+/* TODO: to be further implemented as soon as Prism2 fully supports
+ * GroupAddresses and correct documentation is available */
+void hostap_set_multicast_list_queue(void *data)
+{
+ struct net_device *dev = (struct net_device *) data;
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ if (hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE,
+ local->is_promisc)) {
+ printk(KERN_INFO "%s: %sabling promiscuous mode failed\n",
+ dev->name, local->is_promisc ? "en" : "dis");
+ }
+}
+
+
+static void hostap_set_multicast_list(struct net_device *dev)
+{
+#if 0
+ /* FIX: promiscuous mode seems to be causing a lot of problems with
+ * some station firmware versions (FCSErr frames, invalid MACPort, etc.
+ * corrupted incoming frames). This code is now commented out while the
+ * problems are investigated. */
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ if ((dev->flags & IFF_ALLMULTI) || (dev->flags & IFF_PROMISC)) {
+ local->is_promisc = 1;
+ } else {
+ local->is_promisc = 0;
+ }
+
+ schedule_work(&local->set_multicast_list_queue);
+#endif
+}
+
+
+static int prism2_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if (new_mtu < PRISM2_MIN_MTU || new_mtu > PRISM2_MAX_MTU)
+ return -EINVAL;
+
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+
+static void prism2_tx_timeout(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct hfa384x_regs regs;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ printk(KERN_WARNING "%s Tx timed out! Resetting card\n", dev->name);
+ netif_stop_queue(local->dev);
+
+ local->func->read_regs(dev, &regs);
+ printk(KERN_DEBUG "%s: CMD=%04x EVSTAT=%04x "
+ "OFFSET0=%04x OFFSET1=%04x SWSUPPORT0=%04x\n",
+ dev->name, regs.cmd, regs.evstat, regs.offset0, regs.offset1,
+ regs.swsupport0);
+
+ local->func->schedule_reset(local);
+}
+
+
+void hostap_setup_dev(struct net_device *dev, local_info_t *local,
+ int main_dev)
+{
+ struct hostap_interface *iface;
+
+ iface = netdev_priv(dev);
+ ether_setup(dev);
+
+ /* kernel callbacks */
+ dev->get_stats = hostap_get_stats;
+ if (iface) {
+ /* Currently, we point to the proper spy_data only on
+ * the main_dev. This could be fixed. Jean II */
+ iface->wireless_data.spy_data = &iface->spy_data;
+ dev->wireless_data = &iface->wireless_data;
+ }
+ dev->wireless_handlers =
+ (struct iw_handler_def *) &hostap_iw_handler_def;
+ dev->do_ioctl = hostap_ioctl;
+ dev->open = prism2_open;
+ dev->stop = prism2_close;
+ dev->hard_start_xmit = hostap_data_start_xmit;
+ dev->set_mac_address = prism2_set_mac_address;
+ dev->set_multicast_list = hostap_set_multicast_list;
+ dev->change_mtu = prism2_change_mtu;
+ dev->tx_timeout = prism2_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ dev->mtu = local->mtu;
+ if (!main_dev) {
+ /* use main radio device queue */
+ dev->tx_queue_len = 0;
+ }
+
+ SET_ETHTOOL_OPS(dev, &prism2_ethtool_ops);
+
+ netif_stop_queue(dev);
+}
+
+
+static int hostap_enable_hostapd(local_info_t *local, int rtnl_locked)
+{
+ struct net_device *dev = local->dev;
+
+ if (local->apdev)
+ return -EEXIST;
+
+ printk(KERN_DEBUG "%s: enabling hostapd mode\n", dev->name);
+
+ local->apdev = hostap_add_interface(local, HOSTAP_INTERFACE_AP,
+ rtnl_locked, local->ddev->name,
+ "ap");
+ if (local->apdev == NULL)
+ return -ENOMEM;
+
+ local->apdev->hard_start_xmit = hostap_mgmt_start_xmit;
+ local->apdev->type = ARPHRD_IEEE80211;
+ local->apdev->hard_header_parse = hostap_80211_header_parse;
+
+ return 0;
+}
+
+
+static int hostap_disable_hostapd(local_info_t *local, int rtnl_locked)
+{
+ struct net_device *dev = local->dev;
+
+ printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name);
+
+ hostap_remove_interface(local->apdev, rtnl_locked, 1);
+ local->apdev = NULL;
+
+ return 0;
+}
+
+
+static int hostap_enable_hostapd_sta(local_info_t *local, int rtnl_locked)
+{
+ struct net_device *dev = local->dev;
+
+ if (local->stadev)
+ return -EEXIST;
+
+ printk(KERN_DEBUG "%s: enabling hostapd STA mode\n", dev->name);
+
+ local->stadev = hostap_add_interface(local, HOSTAP_INTERFACE_STA,
+ rtnl_locked, local->ddev->name,
+ "sta");
+ if (local->stadev == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+
+static int hostap_disable_hostapd_sta(local_info_t *local, int rtnl_locked)
+{
+ struct net_device *dev = local->dev;
+
+ printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name);
+
+ hostap_remove_interface(local->stadev, rtnl_locked, 1);
+ local->stadev = NULL;
+
+ return 0;
+}
+
+
+int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked)
+{
+ int ret;
+
+ if (val < 0 || val > 1)
+ return -EINVAL;
+
+ if (local->hostapd == val)
+ return 0;
+
+ if (val) {
+ ret = hostap_enable_hostapd(local, rtnl_locked);
+ if (ret == 0)
+ local->hostapd = 1;
+ } else {
+ local->hostapd = 0;
+ ret = hostap_disable_hostapd(local, rtnl_locked);
+ if (ret != 0)
+ local->hostapd = 1;
+ }
+
+ return ret;
+}
+
+
+int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked)
+{
+ int ret;
+
+ if (val < 0 || val > 1)
+ return -EINVAL;
+
+ if (local->hostapd_sta == val)
+ return 0;
+
+ if (val) {
+ ret = hostap_enable_hostapd_sta(local, rtnl_locked);
+ if (ret == 0)
+ local->hostapd_sta = 1;
+ } else {
+ local->hostapd_sta = 0;
+ ret = hostap_disable_hostapd_sta(local, rtnl_locked);
+ if (ret != 0)
+ local->hostapd_sta = 1;
+ }
+
+
+ return ret;
+}
+
+
+int prism2_update_comms_qual(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int ret = 0;
+ struct hfa384x_comms_quality sq;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ if (!local->sta_fw_ver)
+ ret = -1;
+ else if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) {
+ if (local->func->get_rid(local->dev,
+ HFA384X_RID_DBMCOMMSQUALITY,
+ &sq, sizeof(sq), 1) >= 0) {
+ local->comms_qual = (s16) le16_to_cpu(sq.comm_qual);
+ local->avg_signal = (s16) le16_to_cpu(sq.signal_level);
+ local->avg_noise = (s16) le16_to_cpu(sq.noise_level);
+ local->last_comms_qual_update = jiffies;
+ } else
+ ret = -1;
+ } else {
+ if (local->func->get_rid(local->dev, HFA384X_RID_COMMSQUALITY,
+ &sq, sizeof(sq), 1) >= 0) {
+ local->comms_qual = le16_to_cpu(sq.comm_qual);
+ local->avg_signal = HFA384X_LEVEL_TO_dBm(
+ le16_to_cpu(sq.signal_level));
+ local->avg_noise = HFA384X_LEVEL_TO_dBm(
+ le16_to_cpu(sq.noise_level));
+ local->last_comms_qual_update = jiffies;
+ } else
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype,
+ u8 *body, size_t bodylen)
+{
+ struct sk_buff *skb;
+ struct hostap_ieee80211_mgmt *mgmt;
+ struct hostap_skb_tx_data *meta;
+ struct net_device *dev = local->dev;
+
+ skb = dev_alloc_skb(IEEE80211_MGMT_HDR_LEN + bodylen);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ mgmt = (struct hostap_ieee80211_mgmt *)
+ skb_put(skb, IEEE80211_MGMT_HDR_LEN);
+ memset(mgmt, 0, IEEE80211_MGMT_HDR_LEN);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
+ memcpy(mgmt->da, dst, ETH_ALEN);
+ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, dst, ETH_ALEN);
+ if (body)
+ memcpy(skb_put(skb, bodylen), body, bodylen);
+
+ meta = (struct hostap_skb_tx_data *) skb->cb;
+ memset(meta, 0, sizeof(*meta));
+ meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
+ meta->iface = netdev_priv(dev);
+
+ skb->dev = dev;
+ skb->mac.raw = skb->nh.raw = skb->data;
+ dev_queue_xmit(skb);
+
+ return 0;
+}
+
+
+int prism2_sta_deauth(local_info_t *local, u16 reason)
+{
+ union iwreq_data wrqu;
+ int ret;
+
+ if (local->iw_mode != IW_MODE_INFRA ||
+ memcmp(local->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0 ||
+ memcmp(local->bssid, "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == 0)
+ return 0;
+
+ reason = cpu_to_le16(reason);
+ ret = prism2_sta_send_mgmt(local, local->bssid, IEEE80211_STYPE_DEAUTH,
+ (u8 *) &reason, 2);
+ memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+ wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL);
+ return ret;
+}
+
+
+struct proc_dir_entry *hostap_proc;
+
+static int __init hostap_init(void)
+{
+ if (proc_net != NULL) {
+ hostap_proc = proc_mkdir("hostap", proc_net);
+ if (!hostap_proc)
+ printk(KERN_WARNING "Failed to mkdir "
+ "/proc/net/hostap\n");
+ } else
+ hostap_proc = NULL;
+
+ return 0;
+}
+
+
+static void __exit hostap_exit(void)
+{
+ if (hostap_proc != NULL) {
+ hostap_proc = NULL;
+ remove_proc_entry("hostap", proc_net);
+ }
+}
+
+
+EXPORT_SYMBOL(hostap_set_word);
+EXPORT_SYMBOL(hostap_set_string);
+EXPORT_SYMBOL(hostap_get_porttype);
+EXPORT_SYMBOL(hostap_set_encryption);
+EXPORT_SYMBOL(hostap_set_antsel);
+EXPORT_SYMBOL(hostap_set_roaming);
+EXPORT_SYMBOL(hostap_set_auth_algs);
+EXPORT_SYMBOL(hostap_dump_rx_header);
+EXPORT_SYMBOL(hostap_dump_tx_header);
+EXPORT_SYMBOL(hostap_80211_header_parse);
+EXPORT_SYMBOL(hostap_80211_prism_header_parse);
+EXPORT_SYMBOL(hostap_80211_get_hdrlen);
+EXPORT_SYMBOL(hostap_get_stats);
+EXPORT_SYMBOL(hostap_setup_dev);
+EXPORT_SYMBOL(hostap_proc);
+EXPORT_SYMBOL(hostap_set_multicast_list_queue);
+EXPORT_SYMBOL(hostap_set_hostapd);
+EXPORT_SYMBOL(hostap_set_hostapd_sta);
+EXPORT_SYMBOL(hostap_add_interface);
+EXPORT_SYMBOL(hostap_remove_interface);
+EXPORT_SYMBOL(prism2_update_comms_qual);
+
+module_init(hostap_init);
+module_exit(hostap_exit);
diff --git a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/hostap/hostap.h
new file mode 100644
index 000000000000..5fac89b8ce3a
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap.h
@@ -0,0 +1,57 @@
+#ifndef HOSTAP_H
+#define HOSTAP_H
+
+/* hostap.c */
+
+extern struct proc_dir_entry *hostap_proc;
+
+u16 hostap_tx_callback_register(local_info_t *local,
+ void (*func)(struct sk_buff *, int ok, void *),
+ void *data);
+int hostap_tx_callback_unregister(local_info_t *local, u16 idx);
+int hostap_set_word(struct net_device *dev, int rid, u16 val);
+int hostap_set_string(struct net_device *dev, int rid, const char *val);
+u16 hostap_get_porttype(local_info_t *local);
+int hostap_set_encryption(local_info_t *local);
+int hostap_set_antsel(local_info_t *local);
+int hostap_set_roaming(local_info_t *local);
+int hostap_set_auth_algs(local_info_t *local);
+void hostap_dump_rx_header(const char *name,
+ const struct hfa384x_rx_frame *rx);
+void hostap_dump_tx_header(const char *name,
+ const struct hfa384x_tx_frame *tx);
+int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr);
+int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr);
+int hostap_80211_get_hdrlen(u16 fc);
+struct net_device_stats *hostap_get_stats(struct net_device *dev);
+void hostap_setup_dev(struct net_device *dev, local_info_t *local,
+ int main_dev);
+void hostap_set_multicast_list_queue(void *data);
+int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked);
+int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked);
+void hostap_cleanup(local_info_t *local);
+void hostap_cleanup_handler(void *data);
+struct net_device * hostap_add_interface(struct local_info *local,
+ int type, int rtnl_locked,
+ const char *prefix, const char *name);
+void hostap_remove_interface(struct net_device *dev, int rtnl_locked,
+ int remove_from_list);
+int prism2_update_comms_qual(struct net_device *dev);
+int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype,
+ u8 *body, size_t bodylen);
+int prism2_sta_deauth(local_info_t *local, u16 reason);
+
+
+/* hostap_proc.c */
+
+void hostap_init_proc(local_info_t *local);
+void hostap_remove_proc(local_info_t *local);
+
+
+/* hostap_info.c */
+
+void hostap_info_init(local_info_t *local);
+void hostap_info_process(local_info_t *local, struct sk_buff *skb);
+
+
+#endif /* HOSTAP_H */
diff --git a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/hostap/hostap_80211.h
new file mode 100644
index 000000000000..bf506f50d722
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_80211.h
@@ -0,0 +1,96 @@
+#ifndef HOSTAP_80211_H
+#define HOSTAP_80211_H
+
+struct hostap_ieee80211_mgmt {
+ u16 frame_control;
+ u16 duration;
+ u8 da[6];
+ u8 sa[6];
+ u8 bssid[6];
+ u16 seq_ctrl;
+ union {
+ struct {
+ u16 auth_alg;
+ u16 auth_transaction;
+ u16 status_code;
+ /* possibly followed by Challenge text */
+ u8 variable[0];
+ } __attribute__ ((packed)) auth;
+ struct {
+ u16 reason_code;
+ } __attribute__ ((packed)) deauth;
+ struct {
+ u16 capab_info;
+ u16 listen_interval;
+ /* followed by SSID and Supported rates */
+ u8 variable[0];
+ } __attribute__ ((packed)) assoc_req;
+ struct {
+ u16 capab_info;
+ u16 status_code;
+ u16 aid;
+ /* followed by Supported rates */
+ u8 variable[0];
+ } __attribute__ ((packed)) assoc_resp, reassoc_resp;
+ struct {
+ u16 capab_info;
+ u16 listen_interval;
+ u8 current_ap[6];
+ /* followed by SSID and Supported rates */
+ u8 variable[0];
+ } __attribute__ ((packed)) reassoc_req;
+ struct {
+ u16 reason_code;
+ } __attribute__ ((packed)) disassoc;
+ struct {
+ } __attribute__ ((packed)) probe_req;
+ struct {
+ u8 timestamp[8];
+ u16 beacon_int;
+ u16 capab_info;
+ /* followed by some of SSID, Supported rates,
+ * FH Params, DS Params, CF Params, IBSS Params, TIM */
+ u8 variable[0];
+ } __attribute__ ((packed)) beacon, probe_resp;
+ } u;
+} __attribute__ ((packed));
+
+
+#define IEEE80211_MGMT_HDR_LEN 24
+#define IEEE80211_DATA_HDR3_LEN 24
+#define IEEE80211_DATA_HDR4_LEN 30
+
+
+struct hostap_80211_rx_status {
+ u32 mac_time;
+ u8 signal;
+ u8 noise;
+ u16 rate; /* in 100 kbps */
+};
+
+
+void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats);
+
+
+/* prism2_rx_80211 'type' argument */
+enum {
+ PRISM2_RX_MONITOR, PRISM2_RX_MGMT, PRISM2_RX_NON_ASSOC,
+ PRISM2_RX_NULLFUNC_ACK
+};
+
+int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats, int type);
+void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats);
+void hostap_dump_rx_80211(const char *name, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats);
+
+void hostap_dump_tx_80211(const char *name, struct sk_buff *skb);
+int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev);
+int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev);
+struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
+ struct ieee80211_crypt_data *crypt);
+int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
+
+#endif /* HOSTAP_80211_H */
diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c
new file mode 100644
index 000000000000..b0501243b175
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_80211_rx.c
@@ -0,0 +1,1091 @@
+#include <linux/etherdevice.h>
+
+#include "hostap_80211.h"
+#include "hostap.h"
+
+void hostap_dump_rx_80211(const char *name, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+
+ printk(KERN_DEBUG "%s: RX signal=%d noise=%d rate=%d len=%d "
+ "jiffies=%ld\n",
+ name, rx_stats->signal, rx_stats->noise, rx_stats->rate,
+ skb->len, jiffies);
+
+ if (skb->len < 2)
+ return;
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s",
+ fc, WLAN_FC_GET_TYPE(fc) >> 2, WLAN_FC_GET_STYPE(fc) >> 4,
+ fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "",
+ fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : "");
+
+ if (skb->len < IEEE80211_DATA_HDR3_LEN) {
+ printk("\n");
+ return;
+ }
+
+ printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id),
+ le16_to_cpu(hdr->seq_ctl));
+
+ printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR,
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), MAC2STR(hdr->addr3));
+ if (skb->len >= 30)
+ printk(" A4=" MACSTR, MAC2STR(hdr->addr4));
+ printk("\n");
+}
+
+
+/* Send RX frame to netif with 802.11 (and possible prism) header.
+ * Called from hardware or software IRQ context. */
+int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats, int type)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int hdrlen, phdrlen, head_need, tail_need;
+ u16 fc;
+ int prism_header, ret;
+ struct ieee80211_hdr *hdr;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ dev->last_rx = jiffies;
+
+ if (dev->type == ARPHRD_IEEE80211_PRISM) {
+ if (local->monitor_type == PRISM2_MONITOR_PRISM) {
+ prism_header = 1;
+ phdrlen = sizeof(struct linux_wlan_ng_prism_hdr);
+ } else { /* local->monitor_type == PRISM2_MONITOR_CAPHDR */
+ prism_header = 2;
+ phdrlen = sizeof(struct linux_wlan_ng_cap_hdr);
+ }
+ } else {
+ prism_header = 0;
+ phdrlen = 0;
+ }
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ if (type == PRISM2_RX_MGMT && (fc & IEEE80211_FCTL_VERS)) {
+ printk(KERN_DEBUG "%s: dropped management frame with header "
+ "version %d\n", dev->name, fc & IEEE80211_FCTL_VERS);
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ hdrlen = hostap_80211_get_hdrlen(fc);
+
+ /* check if there is enough room for extra data; if not, expand skb
+ * buffer to be large enough for the changes */
+ head_need = phdrlen;
+ tail_need = 0;
+#ifdef PRISM2_ADD_BOGUS_CRC
+ tail_need += 4;
+#endif /* PRISM2_ADD_BOGUS_CRC */
+
+ head_need -= skb_headroom(skb);
+ tail_need -= skb_tailroom(skb);
+
+ if (head_need > 0 || tail_need > 0) {
+ if (pskb_expand_head(skb, head_need > 0 ? head_need : 0,
+ tail_need > 0 ? tail_need : 0,
+ GFP_ATOMIC)) {
+ printk(KERN_DEBUG "%s: prism2_rx_80211 failed to "
+ "reallocate skb buffer\n", dev->name);
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+ }
+
+ /* We now have an skb with enough head and tail room, so just insert
+ * the extra data */
+
+#ifdef PRISM2_ADD_BOGUS_CRC
+ memset(skb_put(skb, 4), 0xff, 4); /* Prism2 strips CRC */
+#endif /* PRISM2_ADD_BOGUS_CRC */
+
+ if (prism_header == 1) {
+ struct linux_wlan_ng_prism_hdr *hdr;
+ hdr = (struct linux_wlan_ng_prism_hdr *)
+ skb_push(skb, phdrlen);
+ memset(hdr, 0, phdrlen);
+ hdr->msgcode = LWNG_CAP_DID_BASE;
+ hdr->msglen = sizeof(*hdr);
+ memcpy(hdr->devname, dev->name, sizeof(hdr->devname));
+#define LWNG_SETVAL(f,i,s,l,d) \
+hdr->f.did = LWNG_CAP_DID_BASE | (i << 12); \
+hdr->f.status = s; hdr->f.len = l; hdr->f.data = d
+ LWNG_SETVAL(hosttime, 1, 0, 4, jiffies);
+ LWNG_SETVAL(mactime, 2, 0, 4, rx_stats->mac_time);
+ LWNG_SETVAL(channel, 3, 1 /* no value */, 4, 0);
+ LWNG_SETVAL(rssi, 4, 1 /* no value */, 4, 0);
+ LWNG_SETVAL(sq, 5, 1 /* no value */, 4, 0);
+ LWNG_SETVAL(signal, 6, 0, 4, rx_stats->signal);
+ LWNG_SETVAL(noise, 7, 0, 4, rx_stats->noise);
+ LWNG_SETVAL(rate, 8, 0, 4, rx_stats->rate / 5);
+ LWNG_SETVAL(istx, 9, 0, 4, 0);
+ LWNG_SETVAL(frmlen, 10, 0, 4, skb->len - phdrlen);
+#undef LWNG_SETVAL
+ } else if (prism_header == 2) {
+ struct linux_wlan_ng_cap_hdr *hdr;
+ hdr = (struct linux_wlan_ng_cap_hdr *)
+ skb_push(skb, phdrlen);
+ memset(hdr, 0, phdrlen);
+ hdr->version = htonl(LWNG_CAPHDR_VERSION);
+ hdr->length = htonl(phdrlen);
+ hdr->mactime = __cpu_to_be64(rx_stats->mac_time);
+ hdr->hosttime = __cpu_to_be64(jiffies);
+ hdr->phytype = htonl(4); /* dss_dot11_b */
+ hdr->channel = htonl(local->channel);
+ hdr->datarate = htonl(rx_stats->rate);
+ hdr->antenna = htonl(0); /* unknown */
+ hdr->priority = htonl(0); /* unknown */
+ hdr->ssi_type = htonl(3); /* raw */
+ hdr->ssi_signal = htonl(rx_stats->signal);
+ hdr->ssi_noise = htonl(rx_stats->noise);
+ hdr->preamble = htonl(0); /* unknown */
+ hdr->encoding = htonl(1); /* cck */
+ }
+
+ ret = skb->len - phdrlen;
+ skb->dev = dev;
+ skb->mac.raw = skb->data;
+ skb_pull(skb, hdrlen);
+ if (prism_header)
+ skb_pull(skb, phdrlen);
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = __constant_htons(ETH_P_802_2);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ netif_rx(skb);
+
+ return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void monitor_rx(struct net_device *dev, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ struct net_device_stats *stats;
+ int len;
+
+ len = prism2_rx_80211(dev, skb, rx_stats, PRISM2_RX_MONITOR);
+ stats = hostap_get_stats(dev);
+ stats->rx_packets++;
+ stats->rx_bytes += len;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static struct prism2_frag_entry *
+prism2_frag_cache_find(local_info_t *local, unsigned int seq,
+ unsigned int frag, u8 *src, u8 *dst)
+{
+ struct prism2_frag_entry *entry;
+ int i;
+
+ for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) {
+ entry = &local->frag_cache[i];
+ if (entry->skb != NULL &&
+ time_after(jiffies, entry->first_frag_time + 2 * HZ)) {
+ printk(KERN_DEBUG "%s: expiring fragment cache entry "
+ "seq=%u last_frag=%u\n",
+ local->dev->name, entry->seq, entry->last_frag);
+ dev_kfree_skb(entry->skb);
+ entry->skb = NULL;
+ }
+
+ if (entry->skb != NULL && entry->seq == seq &&
+ (entry->last_frag + 1 == frag || frag == -1) &&
+ memcmp(entry->src_addr, src, ETH_ALEN) == 0 &&
+ memcmp(entry->dst_addr, dst, ETH_ALEN) == 0)
+ return entry;
+ }
+
+ return NULL;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static struct sk_buff *
+prism2_frag_cache_get(local_info_t *local, struct ieee80211_hdr *hdr)
+{
+ struct sk_buff *skb = NULL;
+ u16 sc;
+ unsigned int frag, seq;
+ struct prism2_frag_entry *entry;
+
+ sc = le16_to_cpu(hdr->seq_ctl);
+ frag = WLAN_GET_SEQ_FRAG(sc);
+ seq = WLAN_GET_SEQ_SEQ(sc) >> 4;
+
+ if (frag == 0) {
+ /* Reserve enough space to fit maximum frame length */
+ skb = dev_alloc_skb(local->dev->mtu +
+ sizeof(struct ieee80211_hdr) +
+ 8 /* LLC */ +
+ 2 /* alignment */ +
+ 8 /* WEP */ + ETH_ALEN /* WDS */);
+ if (skb == NULL)
+ return NULL;
+
+ entry = &local->frag_cache[local->frag_next_idx];
+ local->frag_next_idx++;
+ if (local->frag_next_idx >= PRISM2_FRAG_CACHE_LEN)
+ local->frag_next_idx = 0;
+
+ if (entry->skb != NULL)
+ dev_kfree_skb(entry->skb);
+
+ entry->first_frag_time = jiffies;
+ entry->seq = seq;
+ entry->last_frag = frag;
+ entry->skb = skb;
+ memcpy(entry->src_addr, hdr->addr2, ETH_ALEN);
+ memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN);
+ } else {
+ /* received a fragment of a frame for which the head fragment
+ * should have already been received */
+ entry = prism2_frag_cache_find(local, seq, frag, hdr->addr2,
+ hdr->addr1);
+ if (entry != NULL) {
+ entry->last_frag = frag;
+ skb = entry->skb;
+ }
+ }
+
+ return skb;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static int prism2_frag_cache_invalidate(local_info_t *local,
+ struct ieee80211_hdr *hdr)
+{
+ u16 sc;
+ unsigned int seq;
+ struct prism2_frag_entry *entry;
+
+ sc = le16_to_cpu(hdr->seq_ctl);
+ seq = WLAN_GET_SEQ_SEQ(sc) >> 4;
+
+ entry = prism2_frag_cache_find(local, seq, -1, hdr->addr2, hdr->addr1);
+
+ if (entry == NULL) {
+ printk(KERN_DEBUG "%s: could not invalidate fragment cache "
+ "entry (seq=%u)\n",
+ local->dev->name, seq);
+ return -1;
+ }
+
+ entry->skb = NULL;
+ return 0;
+}
+
+
+static struct hostap_bss_info *__hostap_get_bss(local_info_t *local, u8 *bssid,
+ u8 *ssid, size_t ssid_len)
+{
+ struct list_head *ptr;
+ struct hostap_bss_info *bss;
+
+ list_for_each(ptr, &local->bss_list) {
+ bss = list_entry(ptr, struct hostap_bss_info, list);
+ if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+ (ssid == NULL ||
+ (ssid_len == bss->ssid_len &&
+ memcmp(ssid, bss->ssid, ssid_len) == 0))) {
+ list_move(&bss->list, &local->bss_list);
+ return bss;
+ }
+ }
+
+ return NULL;
+}
+
+
+static struct hostap_bss_info *__hostap_add_bss(local_info_t *local, u8 *bssid,
+ u8 *ssid, size_t ssid_len)
+{
+ struct hostap_bss_info *bss;
+
+ if (local->num_bss_info >= HOSTAP_MAX_BSS_COUNT) {
+ bss = list_entry(local->bss_list.prev,
+ struct hostap_bss_info, list);
+ list_del(&bss->list);
+ local->num_bss_info--;
+ } else {
+ bss = (struct hostap_bss_info *)
+ kmalloc(sizeof(*bss), GFP_ATOMIC);
+ if (bss == NULL)
+ return NULL;
+ }
+
+ memset(bss, 0, sizeof(*bss));
+ memcpy(bss->bssid, bssid, ETH_ALEN);
+ memcpy(bss->ssid, ssid, ssid_len);
+ bss->ssid_len = ssid_len;
+ local->num_bss_info++;
+ list_add(&bss->list, &local->bss_list);
+ return bss;
+}
+
+
+static void __hostap_expire_bss(local_info_t *local)
+{
+ struct hostap_bss_info *bss;
+
+ while (local->num_bss_info > 0) {
+ bss = list_entry(local->bss_list.prev,
+ struct hostap_bss_info, list);
+ if (!time_after(jiffies, bss->last_update + 60 * HZ))
+ break;
+
+ list_del(&bss->list);
+ local->num_bss_info--;
+ kfree(bss);
+ }
+}
+
+
+/* Both IEEE 802.11 Beacon and Probe Response frames have similar structure, so
+ * the same routine can be used to parse both of them. */
+static void hostap_rx_sta_beacon(local_info_t *local, struct sk_buff *skb,
+ int stype)
+{
+ struct hostap_ieee80211_mgmt *mgmt;
+ int left, chan = 0;
+ u8 *pos;
+ u8 *ssid = NULL, *wpa = NULL, *rsn = NULL;
+ size_t ssid_len = 0, wpa_len = 0, rsn_len = 0;
+ struct hostap_bss_info *bss;
+
+ if (skb->len < IEEE80211_MGMT_HDR_LEN + sizeof(mgmt->u.beacon))
+ return;
+
+ mgmt = (struct hostap_ieee80211_mgmt *) skb->data;
+ pos = mgmt->u.beacon.variable;
+ left = skb->len - (pos - skb->data);
+
+ while (left >= 2) {
+ if (2 + pos[1] > left)
+ return; /* parse failed */
+ switch (*pos) {
+ case WLAN_EID_SSID:
+ ssid = pos + 2;
+ ssid_len = pos[1];
+ break;
+ case WLAN_EID_GENERIC:
+ if (pos[1] >= 4 &&
+ pos[2] == 0x00 && pos[3] == 0x50 &&
+ pos[4] == 0xf2 && pos[5] == 1) {
+ wpa = pos;
+ wpa_len = pos[1] + 2;
+ }
+ break;
+ case WLAN_EID_RSN:
+ rsn = pos;
+ rsn_len = pos[1] + 2;
+ break;
+ case WLAN_EID_DS_PARAMS:
+ if (pos[1] >= 1)
+ chan = pos[2];
+ break;
+ }
+ left -= 2 + pos[1];
+ pos += 2 + pos[1];
+ }
+
+ if (wpa_len > MAX_WPA_IE_LEN)
+ wpa_len = MAX_WPA_IE_LEN;
+ if (rsn_len > MAX_WPA_IE_LEN)
+ rsn_len = MAX_WPA_IE_LEN;
+ if (ssid_len > sizeof(bss->ssid))
+ ssid_len = sizeof(bss->ssid);
+
+ spin_lock(&local->lock);
+ bss = __hostap_get_bss(local, mgmt->bssid, ssid, ssid_len);
+ if (bss == NULL)
+ bss = __hostap_add_bss(local, mgmt->bssid, ssid, ssid_len);
+ if (bss) {
+ bss->last_update = jiffies;
+ bss->count++;
+ bss->capab_info = le16_to_cpu(mgmt->u.beacon.capab_info);
+ if (wpa) {
+ memcpy(bss->wpa_ie, wpa, wpa_len);
+ bss->wpa_ie_len = wpa_len;
+ } else
+ bss->wpa_ie_len = 0;
+ if (rsn) {
+ memcpy(bss->rsn_ie, rsn, rsn_len);
+ bss->rsn_ie_len = rsn_len;
+ } else
+ bss->rsn_ie_len = 0;
+ bss->chan = chan;
+ }
+ __hostap_expire_bss(local);
+ spin_unlock(&local->lock);
+}
+
+
+static inline int
+hostap_rx_frame_mgmt(local_info_t *local, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats, u16 type,
+ u16 stype)
+{
+ if (local->iw_mode == IW_MODE_MASTER) {
+ hostap_update_sta_ps(local, (struct ieee80211_hdr *)
+ skb->data);
+ }
+
+ if (local->hostapd && type == IEEE80211_FTYPE_MGMT) {
+ if (stype == IEEE80211_STYPE_BEACON &&
+ local->iw_mode == IW_MODE_MASTER) {
+ struct sk_buff *skb2;
+ /* Process beacon frames also in kernel driver to
+ * update STA(AP) table statistics */
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2)
+ hostap_rx(skb2->dev, skb2, rx_stats);
+ }
+
+ /* send management frames to the user space daemon for
+ * processing */
+ local->apdevstats.rx_packets++;
+ local->apdevstats.rx_bytes += skb->len;
+ if (local->apdev == NULL)
+ return -1;
+ prism2_rx_80211(local->apdev, skb, rx_stats, PRISM2_RX_MGMT);
+ return 0;
+ }
+
+ if (local->iw_mode == IW_MODE_MASTER) {
+ if (type != IEEE80211_FTYPE_MGMT &&
+ type != IEEE80211_FTYPE_CTL) {
+ printk(KERN_DEBUG "%s: unknown management frame "
+ "(type=0x%02x, stype=0x%02x) dropped\n",
+ skb->dev->name, type >> 2, stype >> 4);
+ return -1;
+ }
+
+ hostap_rx(skb->dev, skb, rx_stats);
+ return 0;
+ } else if (type == IEEE80211_FTYPE_MGMT &&
+ (stype == IEEE80211_STYPE_BEACON ||
+ stype == IEEE80211_STYPE_PROBE_RESP)) {
+ hostap_rx_sta_beacon(local, skb, stype);
+ return -1;
+ } else if (type == IEEE80211_FTYPE_MGMT &&
+ (stype == IEEE80211_STYPE_ASSOC_RESP ||
+ stype == IEEE80211_STYPE_REASSOC_RESP)) {
+ /* Ignore (Re)AssocResp silently since these are not currently
+ * needed but are still received when WPA/RSN mode is enabled.
+ */
+ return -1;
+ } else {
+ printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: dropped unhandled"
+ " management frame in non-Host AP mode (type=%d:%d)\n",
+ skb->dev->name, type >> 2, stype >> 4);
+ return -1;
+ }
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static inline struct net_device *prism2_rx_get_wds(local_info_t *local,
+ u8 *addr)
+{
+ struct hostap_interface *iface = NULL;
+ struct list_head *ptr;
+
+ read_lock_bh(&local->iface_lock);
+ list_for_each(ptr, &local->hostap_interfaces) {
+ iface = list_entry(ptr, struct hostap_interface, list);
+ if (iface->type == HOSTAP_INTERFACE_WDS &&
+ memcmp(iface->u.wds.remote_addr, addr, ETH_ALEN) == 0)
+ break;
+ iface = NULL;
+ }
+ read_unlock_bh(&local->iface_lock);
+
+ return iface ? iface->dev : NULL;
+}
+
+
+static inline int
+hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr *hdr,
+ u16 fc, struct net_device **wds)
+{
+ /* FIX: is this really supposed to accept WDS frames only in Master
+ * mode? What about Repeater or Managed with WDS frames? */
+ if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) !=
+ (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS) &&
+ (local->iw_mode != IW_MODE_MASTER || !(fc & IEEE80211_FCTL_TODS)))
+ return 0; /* not a WDS frame */
+
+ /* Possible WDS frame: either IEEE 802.11 compliant (if FromDS)
+ * or own non-standard frame with 4th address after payload */
+ if (memcmp(hdr->addr1, local->dev->dev_addr, ETH_ALEN) != 0 &&
+ (hdr->addr1[0] != 0xff || hdr->addr1[1] != 0xff ||
+ hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff ||
+ hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) {
+ /* RA (or BSSID) is not ours - drop */
+ PDEBUG(DEBUG_EXTRA, "%s: received WDS frame with "
+ "not own or broadcast %s=" MACSTR "\n",
+ local->dev->name,
+ fc & IEEE80211_FCTL_FROMDS ? "RA" : "BSSID",
+ MAC2STR(hdr->addr1));
+ return -1;
+ }
+
+ /* check if the frame came from a registered WDS connection */
+ *wds = prism2_rx_get_wds(local, hdr->addr2);
+ if (*wds == NULL && fc & IEEE80211_FCTL_FROMDS &&
+ (local->iw_mode != IW_MODE_INFRA ||
+ !(local->wds_type & HOSTAP_WDS_AP_CLIENT) ||
+ memcmp(hdr->addr2, local->bssid, ETH_ALEN) != 0)) {
+ /* require that WDS link has been registered with TA or the
+ * frame is from current AP when using 'AP client mode' */
+ PDEBUG(DEBUG_EXTRA, "%s: received WDS[4 addr] frame "
+ "from unknown TA=" MACSTR "\n",
+ local->dev->name, MAC2STR(hdr->addr2));
+ if (local->ap && local->ap->autom_ap_wds)
+ hostap_wds_link_oper(local, hdr->addr2, WDS_ADD);
+ return -1;
+ }
+
+ if (*wds && !(fc & IEEE80211_FCTL_FROMDS) && local->ap &&
+ hostap_is_sta_assoc(local->ap, hdr->addr2)) {
+ /* STA is actually associated with us even though it has a
+ * registered WDS link. Assume it is in 'AP client' mode.
+ * Since this is a 3-addr frame, assume it is not (bogus) WDS
+ * frame and process it like any normal ToDS frame from
+ * associated STA. */
+ *wds = NULL;
+ }
+
+ return 0;
+}
+
+
+static int hostap_is_eapol_frame(local_info_t *local, struct sk_buff *skb)
+{
+ struct net_device *dev = local->dev;
+ u16 fc, ethertype;
+ struct ieee80211_hdr *hdr;
+ u8 *pos;
+
+ if (skb->len < 24)
+ return 0;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ /* check that the frame is unicast frame to us */
+ if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+ IEEE80211_FCTL_TODS &&
+ memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 &&
+ memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) {
+ /* ToDS frame with own addr BSSID and DA */
+ } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+ IEEE80211_FCTL_FROMDS &&
+ memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+ /* FromDS frame with own addr as DA */
+ } else
+ return 0;
+
+ if (skb->len < 24 + 8)
+ return 0;
+
+ /* check for port access entity Ethernet type */
+ pos = skb->data + 24;
+ ethertype = (pos[6] << 8) | pos[7];
+ if (ethertype == ETH_P_PAE)
+ return 1;
+
+ return 0;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static inline int
+hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb,
+ struct ieee80211_crypt_data *crypt)
+{
+ struct ieee80211_hdr *hdr;
+ int res, hdrlen;
+
+ if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL)
+ return 0;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+ if (local->tkip_countermeasures &&
+ strcmp(crypt->ops->name, "TKIP") == 0) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
+ "received packet from " MACSTR "\n",
+ local->dev->name, MAC2STR(hdr->addr2));
+ }
+ return -1;
+ }
+
+ atomic_inc(&crypt->refcnt);
+ res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ printk(KERN_DEBUG "%s: decryption failed (SA=" MACSTR
+ ") res=%d\n",
+ local->dev->name, MAC2STR(hdr->addr2), res);
+ local->comm_tallies.rx_discards_wep_undecryptable++;
+ return -1;
+ }
+
+ return res;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static inline int
+hostap_rx_frame_decrypt_msdu(local_info_t *local, struct sk_buff *skb,
+ int keyidx, struct ieee80211_crypt_data *crypt)
+{
+ struct ieee80211_hdr *hdr;
+ int res, hdrlen;
+
+ if (crypt == NULL || crypt->ops->decrypt_msdu == NULL)
+ return 0;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+ atomic_inc(&crypt->refcnt);
+ res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv);
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed"
+ " (SA=" MACSTR " keyidx=%d)\n",
+ local->dev->name, MAC2STR(hdr->addr2), keyidx);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* All received frames are sent to this function. @skb contains the frame in
+ * IEEE 802.11 format, i.e., in the format it was sent over air.
+ * This function is called only as a tasklet (software IRQ). */
+void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct ieee80211_hdr *hdr;
+ size_t hdrlen;
+ u16 fc, type, stype, sc;
+ struct net_device *wds = NULL;
+ struct net_device_stats *stats;
+ unsigned int frag;
+ u8 *payload;
+ struct sk_buff *skb2 = NULL;
+ u16 ethertype;
+ int frame_authorized = 0;
+ int from_assoc_ap = 0;
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ struct ieee80211_crypt_data *crypt = NULL;
+ void *sta = NULL;
+ int keyidx = 0;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ iface->stats.rx_packets++;
+ iface->stats.rx_bytes += skb->len;
+
+ /* dev is the master radio device; change this to be the default
+ * virtual interface (this may be changed to WDS device below) */
+ dev = local->ddev;
+ iface = netdev_priv(dev);
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ stats = hostap_get_stats(dev);
+
+ if (skb->len < 10)
+ goto rx_dropped;
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+ sc = le16_to_cpu(hdr->seq_ctl);
+ frag = WLAN_GET_SEQ_FRAG(sc);
+ hdrlen = hostap_80211_get_hdrlen(fc);
+
+ /* Put this code here so that we avoid duplicating it in all
+ * Rx paths. - Jean II */
+#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
+ /* If spy monitoring on */
+ if (iface->spy_data.spy_number > 0) {
+ struct iw_quality wstats;
+ wstats.level = rx_stats->signal;
+ wstats.noise = rx_stats->noise;
+ wstats.updated = 6; /* No qual value */
+ /* Update spy records */
+ wireless_spy_update(dev, hdr->addr2, &wstats);
+ }
+#endif /* IW_WIRELESS_SPY */
+ hostap_update_rx_stats(local->ap, hdr, rx_stats);
+
+ if (local->iw_mode == IW_MODE_MONITOR) {
+ monitor_rx(dev, skb, rx_stats);
+ return;
+ }
+
+ if (local->host_decrypt) {
+ int idx = 0;
+ if (skb->len >= hdrlen + 3)
+ idx = skb->data[hdrlen + 3] >> 6;
+ crypt = local->crypt[idx];
+ sta = NULL;
+
+ /* Use station specific key to override default keys if the
+ * receiver address is a unicast address ("individual RA"). If
+ * bcrx_sta_key parameter is set, station specific key is used
+ * even with broad/multicast targets (this is against IEEE
+ * 802.11, but makes it easier to use different keys with
+ * stations that do not support WEP key mapping). */
+
+ if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key)
+ (void) hostap_handle_sta_crypto(local, hdr, &crypt,
+ &sta);
+
+ /* allow NULL decrypt to indicate an station specific override
+ * for default encryption */
+ if (crypt && (crypt->ops == NULL ||
+ crypt->ops->decrypt_mpdu == NULL))
+ crypt = NULL;
+
+ if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) {
+#if 0
+ /* This seems to be triggered by some (multicast?)
+ * frames from other than current BSS, so just drop the
+ * frames silently instead of filling system log with
+ * these reports. */
+ printk(KERN_DEBUG "%s: WEP decryption failed (not set)"
+ " (SA=" MACSTR ")\n",
+ local->dev->name, MAC2STR(hdr->addr2));
+#endif
+ local->comm_tallies.rx_discards_wep_undecryptable++;
+ goto rx_dropped;
+ }
+ }
+
+ if (type != IEEE80211_FTYPE_DATA) {
+ if (type == IEEE80211_FTYPE_MGMT &&
+ stype == IEEE80211_STYPE_AUTH &&
+ fc & IEEE80211_FCTL_PROTECTED && local->host_decrypt &&
+ (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0)
+ {
+ printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth "
+ "from " MACSTR "\n", dev->name,
+ MAC2STR(hdr->addr2));
+ /* TODO: could inform hostapd about this so that it
+ * could send auth failure report */
+ goto rx_dropped;
+ }
+
+ if (hostap_rx_frame_mgmt(local, skb, rx_stats, type, stype))
+ goto rx_dropped;
+ else
+ goto rx_exit;
+ }
+
+ /* Data frame - extract src/dst addresses */
+ if (skb->len < IEEE80211_DATA_HDR3_LEN)
+ goto rx_dropped;
+
+ switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+ case IEEE80211_FCTL_FROMDS:
+ memcpy(dst, hdr->addr1, ETH_ALEN);
+ memcpy(src, hdr->addr3, ETH_ALEN);
+ break;
+ case IEEE80211_FCTL_TODS:
+ memcpy(dst, hdr->addr3, ETH_ALEN);
+ memcpy(src, hdr->addr2, ETH_ALEN);
+ break;
+ case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+ if (skb->len < IEEE80211_DATA_HDR4_LEN)
+ goto rx_dropped;
+ memcpy(dst, hdr->addr3, ETH_ALEN);
+ memcpy(src, hdr->addr4, ETH_ALEN);
+ break;
+ case 0:
+ memcpy(dst, hdr->addr1, ETH_ALEN);
+ memcpy(src, hdr->addr2, ETH_ALEN);
+ break;
+ }
+
+ if (hostap_rx_frame_wds(local, hdr, fc, &wds))
+ goto rx_dropped;
+ if (wds) {
+ skb->dev = dev = wds;
+ stats = hostap_get_stats(dev);
+ }
+
+ if (local->iw_mode == IW_MODE_MASTER && !wds &&
+ (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+ IEEE80211_FCTL_FROMDS &&
+ local->stadev &&
+ memcmp(hdr->addr2, local->assoc_ap_addr, ETH_ALEN) == 0) {
+ /* Frame from BSSID of the AP for which we are a client */
+ skb->dev = dev = local->stadev;
+ stats = hostap_get_stats(dev);
+ from_assoc_ap = 1;
+ }
+
+ dev->last_rx = jiffies;
+
+ if ((local->iw_mode == IW_MODE_MASTER ||
+ local->iw_mode == IW_MODE_REPEAT) &&
+ !from_assoc_ap) {
+ switch (hostap_handle_sta_rx(local, dev, skb, rx_stats,
+ wds != NULL)) {
+ case AP_RX_CONTINUE_NOT_AUTHORIZED:
+ frame_authorized = 0;
+ break;
+ case AP_RX_CONTINUE:
+ frame_authorized = 1;
+ break;
+ case AP_RX_DROP:
+ goto rx_dropped;
+ case AP_RX_EXIT:
+ goto rx_exit;
+ }
+ }
+
+ /* Nullfunc frames may have PS-bit set, so they must be passed to
+ * hostap_handle_sta_rx() before being dropped here. */
+ if (stype != IEEE80211_STYPE_DATA &&
+ stype != IEEE80211_STYPE_DATA_CFACK &&
+ stype != IEEE80211_STYPE_DATA_CFPOLL &&
+ stype != IEEE80211_STYPE_DATA_CFACKPOLL) {
+ if (stype != IEEE80211_STYPE_NULLFUNC)
+ printk(KERN_DEBUG "%s: RX: dropped data frame "
+ "with no data (type=0x%02x, subtype=0x%02x)\n",
+ dev->name, type >> 2, stype >> 4);
+ goto rx_dropped;
+ }
+
+ /* skb: hdr + (possibly fragmented, possibly encrypted) payload */
+
+ if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+ (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0)
+ goto rx_dropped;
+ hdr = (struct ieee80211_hdr *) skb->data;
+
+ /* skb: hdr + (possibly fragmented) plaintext payload */
+
+ if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+ (frag != 0 || (fc & IEEE80211_FCTL_MOREFRAGS))) {
+ int flen;
+ struct sk_buff *frag_skb =
+ prism2_frag_cache_get(local, hdr);
+ if (!frag_skb) {
+ printk(KERN_DEBUG "%s: Rx cannot get skb from "
+ "fragment cache (morefrag=%d seq=%u frag=%u)\n",
+ dev->name, (fc & IEEE80211_FCTL_MOREFRAGS) != 0,
+ WLAN_GET_SEQ_SEQ(sc) >> 4, frag);
+ goto rx_dropped;
+ }
+
+ flen = skb->len;
+ if (frag != 0)
+ flen -= hdrlen;
+
+ if (frag_skb->tail + flen > frag_skb->end) {
+ printk(KERN_WARNING "%s: host decrypted and "
+ "reassembled frame did not fit skb\n",
+ dev->name);
+ prism2_frag_cache_invalidate(local, hdr);
+ goto rx_dropped;
+ }
+
+ if (frag == 0) {
+ /* copy first fragment (including full headers) into
+ * beginning of the fragment cache skb */
+ memcpy(skb_put(frag_skb, flen), skb->data, flen);
+ } else {
+ /* append frame payload to the end of the fragment
+ * cache skb */
+ memcpy(skb_put(frag_skb, flen), skb->data + hdrlen,
+ flen);
+ }
+ dev_kfree_skb(skb);
+ skb = NULL;
+
+ if (fc & IEEE80211_FCTL_MOREFRAGS) {
+ /* more fragments expected - leave the skb in fragment
+ * cache for now; it will be delivered to upper layers
+ * after all fragments have been received */
+ goto rx_exit;
+ }
+
+ /* this was the last fragment and the frame will be
+ * delivered, so remove skb from fragment cache */
+ skb = frag_skb;
+ hdr = (struct ieee80211_hdr *) skb->data;
+ prism2_frag_cache_invalidate(local, hdr);
+ }
+
+ /* skb: hdr + (possible reassembled) full MSDU payload; possibly still
+ * encrypted/authenticated */
+
+ if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+ hostap_rx_frame_decrypt_msdu(local, skb, keyidx, crypt))
+ goto rx_dropped;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !local->open_wep) {
+ if (local->ieee_802_1x &&
+ hostap_is_eapol_frame(local, skb)) {
+ /* pass unencrypted EAPOL frames even if encryption is
+ * configured */
+ PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X - passing "
+ "unencrypted EAPOL frame\n", local->dev->name);
+ } else {
+ printk(KERN_DEBUG "%s: encryption configured, but RX "
+ "frame not encrypted (SA=" MACSTR ")\n",
+ local->dev->name, MAC2STR(hdr->addr2));
+ goto rx_dropped;
+ }
+ }
+
+ if (local->drop_unencrypted && !(fc & IEEE80211_FCTL_PROTECTED) &&
+ !hostap_is_eapol_frame(local, skb)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: dropped unencrypted RX data "
+ "frame from " MACSTR " (drop_unencrypted=1)\n",
+ dev->name, MAC2STR(hdr->addr2));
+ }
+ goto rx_dropped;
+ }
+
+ /* skb: hdr + (possible reassembled) full plaintext payload */
+
+ payload = skb->data + hdrlen;
+ ethertype = (payload[6] << 8) | payload[7];
+
+ /* If IEEE 802.1X is used, check whether the port is authorized to send
+ * the received frame. */
+ if (local->ieee_802_1x && local->iw_mode == IW_MODE_MASTER) {
+ if (ethertype == ETH_P_PAE) {
+ PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X frame\n",
+ dev->name);
+ if (local->hostapd && local->apdev) {
+ /* Send IEEE 802.1X frames to the user
+ * space daemon for processing */
+ prism2_rx_80211(local->apdev, skb, rx_stats,
+ PRISM2_RX_MGMT);
+ local->apdevstats.rx_packets++;
+ local->apdevstats.rx_bytes += skb->len;
+ goto rx_exit;
+ }
+ } else if (!frame_authorized) {
+ printk(KERN_DEBUG "%s: dropped frame from "
+ "unauthorized port (IEEE 802.1X): "
+ "ethertype=0x%04x\n",
+ dev->name, ethertype);
+ goto rx_dropped;
+ }
+ }
+
+ /* convert hdr + possible LLC headers into Ethernet header */
+ if (skb->len - hdrlen >= 8 &&
+ ((memcmp(payload, rfc1042_header, 6) == 0 &&
+ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+ memcmp(payload, bridge_tunnel_header, 6) == 0)) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and
+ * replace EtherType */
+ skb_pull(skb, hdrlen + 6);
+ memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+ } else {
+ u16 len;
+ /* Leave Ethernet header part of hdr and full payload */
+ skb_pull(skb, hdrlen);
+ len = htons(skb->len);
+ memcpy(skb_push(skb, 2), &len, 2);
+ memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+ }
+
+ if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+ IEEE80211_FCTL_TODS) &&
+ skb->len >= ETH_HLEN + ETH_ALEN) {
+ /* Non-standard frame: get addr4 from its bogus location after
+ * the payload */
+ memcpy(skb->data + ETH_ALEN,
+ skb->data + skb->len - ETH_ALEN, ETH_ALEN);
+ skb_trim(skb, skb->len - ETH_ALEN);
+ }
+
+ stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+
+ if (local->iw_mode == IW_MODE_MASTER && !wds &&
+ local->ap->bridge_packets) {
+ if (dst[0] & 0x01) {
+ /* copy multicast frame both to the higher layers and
+ * to the wireless media */
+ local->ap->bridged_multicast++;
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2 == NULL)
+ printk(KERN_DEBUG "%s: skb_clone failed for "
+ "multicast frame\n", dev->name);
+ } else if (hostap_is_sta_authorized(local->ap, dst)) {
+ /* send frame directly to the associated STA using
+ * wireless media and not passing to higher layers */
+ local->ap->bridged_unicast++;
+ skb2 = skb;
+ skb = NULL;
+ }
+ }
+
+ if (skb2 != NULL) {
+ /* send to wireless media */
+ skb2->protocol = __constant_htons(ETH_P_802_3);
+ skb2->mac.raw = skb2->nh.raw = skb2->data;
+ /* skb2->nh.raw = skb2->data + ETH_HLEN; */
+ skb2->dev = dev;
+ dev_queue_xmit(skb2);
+ }
+
+ if (skb) {
+ skb->protocol = eth_type_trans(skb, dev);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ skb->dev = dev;
+ netif_rx(skb);
+ }
+
+ rx_exit:
+ if (sta)
+ hostap_handle_sta_release(sta);
+ return;
+
+ rx_dropped:
+ dev_kfree_skb(skb);
+
+ stats->rx_dropped++;
+ goto rx_exit;
+}
+
+
+EXPORT_SYMBOL(hostap_80211_rx);
diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c
new file mode 100644
index 000000000000..6358015f6526
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_80211_tx.c
@@ -0,0 +1,524 @@
+void hostap_dump_tx_80211(const char *name, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+
+ printk(KERN_DEBUG "%s: TX len=%d jiffies=%ld\n",
+ name, skb->len, jiffies);
+
+ if (skb->len < 2)
+ return;
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s",
+ fc, WLAN_FC_GET_TYPE(fc) >> 2, WLAN_FC_GET_STYPE(fc) >> 4,
+ fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "",
+ fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : "");
+
+ if (skb->len < IEEE80211_DATA_HDR3_LEN) {
+ printk("\n");
+ return;
+ }
+
+ printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id),
+ le16_to_cpu(hdr->seq_ctl));
+
+ printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR,
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), MAC2STR(hdr->addr3));
+ if (skb->len >= 30)
+ printk(" A4=" MACSTR, MAC2STR(hdr->addr4));
+ printk("\n");
+}
+
+
+/* hard_start_xmit function for data interfaces (wlan#, wlan#wds#, wlan#sta)
+ * Convert Ethernet header into a suitable IEEE 802.11 header depending on
+ * device configuration. */
+int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int need_headroom, need_tailroom = 0;
+ struct ieee80211_hdr hdr;
+ u16 fc, ethertype = 0;
+ enum {
+ WDS_NO = 0, WDS_OWN_FRAME, WDS_COMPLIANT_FRAME
+ } use_wds = WDS_NO;
+ u8 *encaps_data;
+ int hdr_len, encaps_len, skip_header_bytes;
+ int to_assoc_ap = 0;
+ struct hostap_skb_tx_data *meta;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (skb->len < ETH_HLEN) {
+ printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb "
+ "(len=%d)\n", dev->name, skb->len);
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (local->ddev != dev) {
+ use_wds = (local->iw_mode == IW_MODE_MASTER &&
+ !(local->wds_type & HOSTAP_WDS_STANDARD_FRAME)) ?
+ WDS_OWN_FRAME : WDS_COMPLIANT_FRAME;
+ if (dev == local->stadev) {
+ to_assoc_ap = 1;
+ use_wds = WDS_NO;
+ } else if (dev == local->apdev) {
+ printk(KERN_DEBUG "%s: prism2_tx: trying to use "
+ "AP device with Ethernet net dev\n", dev->name);
+ kfree_skb(skb);
+ return 0;
+ }
+ } else {
+ if (local->iw_mode == IW_MODE_REPEAT) {
+ printk(KERN_DEBUG "%s: prism2_tx: trying to use "
+ "non-WDS link in Repeater mode\n", dev->name);
+ kfree_skb(skb);
+ return 0;
+ } else if (local->iw_mode == IW_MODE_INFRA &&
+ (local->wds_type & HOSTAP_WDS_AP_CLIENT) &&
+ memcmp(skb->data + ETH_ALEN, dev->dev_addr,
+ ETH_ALEN) != 0) {
+ /* AP client mode: send frames with foreign src addr
+ * using 4-addr WDS frames */
+ use_wds = WDS_COMPLIANT_FRAME;
+ }
+ }
+
+ /* Incoming skb->data: dst_addr[6], src_addr[6], proto[2], payload
+ * ==>
+ * Prism2 TX frame with 802.11 header:
+ * txdesc (address order depending on used mode; includes dst_addr and
+ * src_addr), possible encapsulation (RFC1042/Bridge-Tunnel;
+ * proto[2], payload {, possible addr4[6]} */
+
+ ethertype = (skb->data[12] << 8) | skb->data[13];
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ /* Length of data after IEEE 802.11 header */
+ encaps_data = NULL;
+ encaps_len = 0;
+ skip_header_bytes = ETH_HLEN;
+ if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
+ encaps_data = bridge_tunnel_header;
+ encaps_len = sizeof(bridge_tunnel_header);
+ skip_header_bytes -= 2;
+ } else if (ethertype >= 0x600) {
+ encaps_data = rfc1042_header;
+ encaps_len = sizeof(rfc1042_header);
+ skip_header_bytes -= 2;
+ }
+
+ fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
+ hdr_len = IEEE80211_DATA_HDR3_LEN;
+
+ if (use_wds != WDS_NO) {
+ /* Note! Prism2 station firmware has problems with sending real
+ * 802.11 frames with four addresses; until these problems can
+ * be fixed or worked around, 4-addr frames needed for WDS are
+ * using incompatible format: FromDS flag is not set and the
+ * fourth address is added after the frame payload; it is
+ * assumed, that the receiving station knows how to handle this
+ * frame format */
+
+ if (use_wds == WDS_COMPLIANT_FRAME) {
+ fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
+ /* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA,
+ * Addr4 = SA */
+ memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+ hdr_len += ETH_ALEN;
+ } else {
+ /* bogus 4-addr format to workaround Prism2 station
+ * f/w bug */
+ fc |= IEEE80211_FCTL_TODS;
+ /* From DS: Addr1 = DA (used as RA),
+ * Addr2 = BSSID (used as TA), Addr3 = SA (used as DA),
+ */
+
+ /* SA from skb->data + ETH_ALEN will be added after
+ * frame payload; use hdr.addr4 as a temporary buffer
+ */
+ memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+ need_tailroom += ETH_ALEN;
+ }
+
+ /* send broadcast and multicast frames to broadcast RA, if
+ * configured; otherwise, use unicast RA of the WDS link */
+ if ((local->wds_type & HOSTAP_WDS_BROADCAST_RA) &&
+ skb->data[0] & 0x01)
+ memset(&hdr.addr1, 0xff, ETH_ALEN);
+ else if (iface->type == HOSTAP_INTERFACE_WDS)
+ memcpy(&hdr.addr1, iface->u.wds.remote_addr,
+ ETH_ALEN);
+ else
+ memcpy(&hdr.addr1, local->bssid, ETH_ALEN);
+ memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
+ memcpy(&hdr.addr3, skb->data, ETH_ALEN);
+ } else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) {
+ fc |= IEEE80211_FCTL_FROMDS;
+ /* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */
+ memcpy(&hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
+ memcpy(&hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
+ } else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) {
+ fc |= IEEE80211_FCTL_TODS;
+ /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
+ memcpy(&hdr.addr1, to_assoc_ap ?
+ local->assoc_ap_addr : local->bssid, ETH_ALEN);
+ memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memcpy(&hdr.addr3, skb->data, ETH_ALEN);
+ } else if (local->iw_mode == IW_MODE_ADHOC) {
+ /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
+ memcpy(&hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memcpy(&hdr.addr3, local->bssid, ETH_ALEN);
+ }
+
+ hdr.frame_ctl = cpu_to_le16(fc);
+
+ skb_pull(skb, skip_header_bytes);
+ need_headroom = local->func->need_tx_headroom + hdr_len + encaps_len;
+ if (skb_tailroom(skb) < need_tailroom) {
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (skb == NULL) {
+ iface->stats.tx_dropped++;
+ return 0;
+ }
+ if (pskb_expand_head(skb, need_headroom, need_tailroom,
+ GFP_ATOMIC)) {
+ kfree_skb(skb);
+ iface->stats.tx_dropped++;
+ return 0;
+ }
+ } else if (skb_headroom(skb) < need_headroom) {
+ struct sk_buff *tmp = skb;
+ skb = skb_realloc_headroom(skb, need_headroom);
+ kfree_skb(tmp);
+ if (skb == NULL) {
+ iface->stats.tx_dropped++;
+ return 0;
+ }
+ } else {
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (skb == NULL) {
+ iface->stats.tx_dropped++;
+ return 0;
+ }
+ }
+
+ if (encaps_data)
+ memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
+ memcpy(skb_push(skb, hdr_len), &hdr, hdr_len);
+ if (use_wds == WDS_OWN_FRAME) {
+ memcpy(skb_put(skb, ETH_ALEN), &hdr.addr4, ETH_ALEN);
+ }
+
+ iface->stats.tx_packets++;
+ iface->stats.tx_bytes += skb->len;
+
+ skb->mac.raw = skb->data;
+ meta = (struct hostap_skb_tx_data *) skb->cb;
+ memset(meta, 0, sizeof(*meta));
+ meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
+ if (use_wds)
+ meta->flags |= HOSTAP_TX_FLAGS_WDS;
+ meta->ethertype = ethertype;
+ meta->iface = iface;
+
+ /* Send IEEE 802.11 encapsulated frame using the master radio device */
+ skb->dev = local->dev;
+ dev_queue_xmit(skb);
+ return 0;
+}
+
+
+/* hard_start_xmit function for hostapd wlan#ap interfaces */
+int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct hostap_skb_tx_data *meta;
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (skb->len < 10) {
+ printk(KERN_DEBUG "%s: hostap_mgmt_start_xmit: short skb "
+ "(len=%d)\n", dev->name, skb->len);
+ kfree_skb(skb);
+ return 0;
+ }
+
+ iface->stats.tx_packets++;
+ iface->stats.tx_bytes += skb->len;
+
+ meta = (struct hostap_skb_tx_data *) skb->cb;
+ memset(meta, 0, sizeof(*meta));
+ meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
+ meta->iface = iface;
+
+ if (skb->len >= IEEE80211_DATA_HDR3_LEN + sizeof(rfc1042_header) + 2) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
+ WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_DATA) {
+ u8 *pos = &skb->data[IEEE80211_DATA_HDR3_LEN +
+ sizeof(rfc1042_header)];
+ meta->ethertype = (pos[0] << 8) | pos[1];
+ }
+ }
+
+ /* Send IEEE 802.11 encapsulated frame using the master radio device */
+ skb->dev = local->dev;
+ dev_queue_xmit(skb);
+ return 0;
+}
+
+
+/* Called only from software IRQ */
+struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
+ struct ieee80211_crypt_data *crypt)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+ int hdr_len, res;
+
+ iface = netdev_priv(skb->dev);
+ local = iface->local;
+
+ if (skb->len < IEEE80211_DATA_HDR3_LEN) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ if (local->tkip_countermeasures &&
+ crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
+ "TX packet to " MACSTR "\n",
+ local->dev->name, MAC2STR(hdr->addr1));
+ }
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (skb == NULL)
+ return NULL;
+
+ if ((skb_headroom(skb) < crypt->ops->extra_prefix_len ||
+ skb_tailroom(skb) < crypt->ops->extra_postfix_len) &&
+ pskb_expand_head(skb, crypt->ops->extra_prefix_len,
+ crypt->ops->extra_postfix_len, GFP_ATOMIC)) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ hdr_len = hostap_80211_get_hdrlen(fc);
+
+ /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so
+ * call both MSDU and MPDU encryption functions from here. */
+ atomic_inc(&crypt->refcnt);
+ res = 0;
+ if (crypt->ops->encrypt_msdu)
+ res = crypt->ops->encrypt_msdu(skb, hdr_len, crypt->priv);
+ if (res == 0 && crypt->ops->encrypt_mpdu)
+ res = crypt->ops->encrypt_mpdu(skb, hdr_len, crypt->priv);
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ return skb;
+}
+
+
+/* hard_start_xmit function for master radio interface wifi#.
+ * AP processing (TX rate control, power save buffering, etc.).
+ * Use hardware TX function to send the frame. */
+int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int ret = 1;
+ u16 fc;
+ struct hostap_tx_data tx;
+ ap_tx_ret tx_ret;
+ struct hostap_skb_tx_data *meta;
+ int no_encrypt = 0;
+ struct ieee80211_hdr *hdr;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ tx.skb = skb;
+ tx.sta_ptr = NULL;
+
+ meta = (struct hostap_skb_tx_data *) skb->cb;
+ if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) {
+ printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, "
+ "expected 0x%08x)\n",
+ dev->name, meta->magic, HOSTAP_SKB_TX_DATA_MAGIC);
+ ret = 0;
+ iface->stats.tx_dropped++;
+ goto fail;
+ }
+
+ if (local->host_encrypt) {
+ /* Set crypt to default algorithm and key; will be replaced in
+ * AP code if STA has own alg/key */
+ tx.crypt = local->crypt[local->tx_keyidx];
+ tx.host_encrypt = 1;
+ } else {
+ tx.crypt = NULL;
+ tx.host_encrypt = 0;
+ }
+
+ if (skb->len < 24) {
+ printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb "
+ "(len=%d)\n", dev->name, skb->len);
+ ret = 0;
+ iface->stats.tx_dropped++;
+ goto fail;
+ }
+
+ /* FIX (?):
+ * Wi-Fi 802.11b test plan suggests that AP should ignore power save
+ * bit in authentication and (re)association frames and assume tha
+ * STA remains awake for the response. */
+ tx_ret = hostap_handle_sta_tx(local, &tx);
+ skb = tx.skb;
+ meta = (struct hostap_skb_tx_data *) skb->cb;
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ switch (tx_ret) {
+ case AP_TX_CONTINUE:
+ break;
+ case AP_TX_CONTINUE_NOT_AUTHORIZED:
+ if (local->ieee_802_1x &&
+ WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
+ meta->ethertype != ETH_P_PAE &&
+ !(meta->flags & HOSTAP_TX_FLAGS_WDS)) {
+ printk(KERN_DEBUG "%s: dropped frame to unauthorized "
+ "port (IEEE 802.1X): ethertype=0x%04x\n",
+ dev->name, meta->ethertype);
+ hostap_dump_tx_80211(dev->name, skb);
+
+ ret = 0; /* drop packet */
+ iface->stats.tx_dropped++;
+ goto fail;
+ }
+ break;
+ case AP_TX_DROP:
+ ret = 0; /* drop packet */
+ iface->stats.tx_dropped++;
+ goto fail;
+ case AP_TX_RETRY:
+ goto fail;
+ case AP_TX_BUFFERED:
+ /* do not free skb here, it will be freed when the
+ * buffered frame is sent/timed out */
+ ret = 0;
+ goto tx_exit;
+ }
+
+ /* Request TX callback if protocol version is 2 in 802.11 header;
+ * this version 2 is a special case used between hostapd and kernel
+ * driver */
+ if (((fc & IEEE80211_FCTL_VERS) == BIT(1)) &&
+ local->ap && local->ap->tx_callback_idx && meta->tx_cb_idx == 0) {
+ meta->tx_cb_idx = local->ap->tx_callback_idx;
+
+ /* remove special version from the frame header */
+ fc &= ~IEEE80211_FCTL_VERS;
+ hdr->frame_ctl = cpu_to_le16(fc);
+ }
+
+ if (WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_DATA) {
+ no_encrypt = 1;
+ tx.crypt = NULL;
+ }
+
+ if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt &&
+ !(fc & IEEE80211_FCTL_VERS)) {
+ no_encrypt = 1;
+ PDEBUG(DEBUG_EXTRA2, "%s: TX: IEEE 802.1X - passing "
+ "unencrypted EAPOL frame\n", dev->name);
+ tx.crypt = NULL; /* no encryption for IEEE 802.1X frames */
+ }
+
+ if (tx.crypt && (!tx.crypt->ops || !tx.crypt->ops->encrypt_mpdu))
+ tx.crypt = NULL;
+ else if ((tx.crypt || local->crypt[local->tx_keyidx]) && !no_encrypt) {
+ /* Add ISWEP flag both for firmware and host based encryption
+ */
+ fc |= IEEE80211_FCTL_PROTECTED;
+ hdr->frame_ctl = cpu_to_le16(fc);
+ } else if (local->drop_unencrypted &&
+ WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
+ meta->ethertype != ETH_P_PAE) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: dropped unencrypted TX data "
+ "frame (drop_unencrypted=1)\n", dev->name);
+ }
+ iface->stats.tx_dropped++;
+ ret = 0;
+ goto fail;
+ }
+
+ if (tx.crypt) {
+ skb = hostap_tx_encrypt(skb, tx.crypt);
+ if (skb == NULL) {
+ printk(KERN_DEBUG "%s: TX - encryption failed\n",
+ dev->name);
+ ret = 0;
+ goto fail;
+ }
+ meta = (struct hostap_skb_tx_data *) skb->cb;
+ if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) {
+ printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, "
+ "expected 0x%08x) after hostap_tx_encrypt\n",
+ dev->name, meta->magic,
+ HOSTAP_SKB_TX_DATA_MAGIC);
+ ret = 0;
+ iface->stats.tx_dropped++;
+ goto fail;
+ }
+ }
+
+ if (local->func->tx == NULL || local->func->tx(skb, dev)) {
+ ret = 0;
+ iface->stats.tx_dropped++;
+ } else {
+ ret = 0;
+ iface->stats.tx_packets++;
+ iface->stats.tx_bytes += skb->len;
+ }
+
+ fail:
+ if (!ret && skb)
+ dev_kfree_skb(skb);
+ tx_exit:
+ if (tx.sta_ptr)
+ hostap_handle_sta_release(tx.sta_ptr);
+ return ret;
+}
+
+
+EXPORT_SYMBOL(hostap_dump_tx_80211);
+EXPORT_SYMBOL(hostap_tx_encrypt);
+EXPORT_SYMBOL(hostap_master_start_xmit);
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c
new file mode 100644
index 000000000000..930cef8367f2
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_ap.c
@@ -0,0 +1,3288 @@
+/*
+ * Intersil Prism2 driver with Host AP (software access point) support
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This file is to be included into hostap.c when S/W AP functionality is
+ * compiled.
+ *
+ * AP: FIX:
+ * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from
+ * unauthenticated STA, send deauth. frame (8802.11: 5.5)
+ * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received
+ * from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5)
+ * - if unicast Class 3 received from unauthenticated STA, send deauth. frame
+ * (8802.11: 5.5)
+ */
+
+static int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL,
+ DEF_INTS };
+module_param_array(other_ap_policy, int, NULL, 0444);
+MODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)");
+
+static int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC,
+ DEF_INTS };
+module_param_array(ap_max_inactivity, int, NULL, 0444);
+MODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station "
+ "inactivity");
+
+static int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS };
+module_param_array(ap_bridge_packets, int, NULL, 0444);
+MODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between "
+ "stations");
+
+static int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS };
+module_param_array(autom_ap_wds, int, NULL, 0444);
+MODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs "
+ "automatically");
+
+
+static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta);
+static void hostap_event_expired_sta(struct net_device *dev,
+ struct sta_info *sta);
+static void handle_add_proc_queue(void *data);
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+static void handle_wds_oper_queue(void *data);
+static void prism2_send_mgmt(struct net_device *dev,
+ u16 type_subtype, char *body,
+ int body_len, u8 *addr, u16 tx_cb_idx);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+static int ap_debug_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ struct ap_data *ap = (struct ap_data *) data;
+
+ if (off != 0) {
+ *eof = 1;
+ return 0;
+ }
+
+ p += sprintf(p, "BridgedUnicastFrames=%u\n", ap->bridged_unicast);
+ p += sprintf(p, "BridgedMulticastFrames=%u\n", ap->bridged_multicast);
+ p += sprintf(p, "max_inactivity=%u\n", ap->max_inactivity / HZ);
+ p += sprintf(p, "bridge_packets=%u\n", ap->bridge_packets);
+ p += sprintf(p, "nullfunc_ack=%u\n", ap->nullfunc_ack);
+ p += sprintf(p, "autom_ap_wds=%u\n", ap->autom_ap_wds);
+ p += sprintf(p, "auth_algs=%u\n", ap->local->auth_algs);
+ p += sprintf(p, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc);
+
+ return (p - page);
+}
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+
+static void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta)
+{
+ sta->hnext = ap->sta_hash[STA_HASH(sta->addr)];
+ ap->sta_hash[STA_HASH(sta->addr)] = sta;
+}
+
+static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta)
+{
+ struct sta_info *s;
+
+ s = ap->sta_hash[STA_HASH(sta->addr)];
+ if (s == NULL) return;
+ if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) {
+ ap->sta_hash[STA_HASH(sta->addr)] = s->hnext;
+ return;
+ }
+
+ while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, ETH_ALEN)
+ != 0)
+ s = s->hnext;
+ if (s->hnext != NULL)
+ s->hnext = s->hnext->hnext;
+ else
+ printk("AP: could not remove STA " MACSTR " from hash table\n",
+ MAC2STR(sta->addr));
+}
+
+static void ap_free_sta(struct ap_data *ap, struct sta_info *sta)
+{
+ if (sta->ap && sta->local)
+ hostap_event_expired_sta(sta->local->dev, sta);
+
+ if (ap->proc != NULL) {
+ char name[20];
+ sprintf(name, MACSTR, MAC2STR(sta->addr));
+ remove_proc_entry(name, ap->proc);
+ }
+
+ if (sta->crypt) {
+ sta->crypt->ops->deinit(sta->crypt->priv);
+ kfree(sta->crypt);
+ sta->crypt = NULL;
+ }
+
+ skb_queue_purge(&sta->tx_buf);
+
+ ap->num_sta--;
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ if (sta->aid > 0)
+ ap->sta_aid[sta->aid - 1] = NULL;
+
+ if (!sta->ap && sta->u.sta.challenge)
+ kfree(sta->u.sta.challenge);
+ del_timer(&sta->timer);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ kfree(sta);
+}
+
+
+static void hostap_set_tim(local_info_t *local, int aid, int set)
+{
+ if (local->func->set_tim)
+ local->func->set_tim(local->dev, aid, set);
+}
+
+
+static void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta)
+{
+ union iwreq_data wrqu;
+ memset(&wrqu, 0, sizeof(wrqu));
+ memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL);
+}
+
+
+static void hostap_event_expired_sta(struct net_device *dev,
+ struct sta_info *sta)
+{
+ union iwreq_data wrqu;
+ memset(&wrqu, 0, sizeof(wrqu));
+ memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+static void ap_handle_timer(unsigned long data)
+{
+ struct sta_info *sta = (struct sta_info *) data;
+ local_info_t *local;
+ struct ap_data *ap;
+ unsigned long next_time = 0;
+ int was_assoc;
+
+ if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) {
+ PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n");
+ return;
+ }
+
+ local = sta->local;
+ ap = local->ap;
+ was_assoc = sta->flags & WLAN_STA_ASSOC;
+
+ if (atomic_read(&sta->users) != 0)
+ next_time = jiffies + HZ;
+ else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH))
+ next_time = jiffies + ap->max_inactivity;
+
+ if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) {
+ /* station activity detected; reset timeout state */
+ sta->timeout_next = STA_NULLFUNC;
+ next_time = sta->last_rx + ap->max_inactivity;
+ } else if (sta->timeout_next == STA_DISASSOC &&
+ !(sta->flags & WLAN_STA_PENDING_POLL)) {
+ /* STA ACKed data nullfunc frame poll */
+ sta->timeout_next = STA_NULLFUNC;
+ next_time = jiffies + ap->max_inactivity;
+ }
+
+ if (next_time) {
+ sta->timer.expires = next_time;
+ add_timer(&sta->timer);
+ return;
+ }
+
+ if (sta->ap)
+ sta->timeout_next = STA_DEAUTH;
+
+ if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) {
+ spin_lock(&ap->sta_table_lock);
+ ap_sta_hash_del(ap, sta);
+ list_del(&sta->list);
+ spin_unlock(&ap->sta_table_lock);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ } else if (sta->timeout_next == STA_DISASSOC)
+ sta->flags &= ~WLAN_STA_ASSOC;
+
+ if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+ hostap_event_expired_sta(local->dev, sta);
+
+ if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 &&
+ !skb_queue_empty(&sta->tx_buf)) {
+ hostap_set_tim(local, sta->aid, 0);
+ sta->flags &= ~WLAN_STA_TIM;
+ }
+
+ if (sta->ap) {
+ if (ap->autom_ap_wds) {
+ PDEBUG(DEBUG_AP, "%s: removing automatic WDS "
+ "connection to AP " MACSTR "\n",
+ local->dev->name, MAC2STR(sta->addr));
+ hostap_wds_link_oper(local, sta->addr, WDS_DEL);
+ }
+ } else if (sta->timeout_next == STA_NULLFUNC) {
+ /* send data frame to poll STA and check whether this frame
+ * is ACKed */
+ /* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but
+ * it is apparently not retried so TX Exc events are not
+ * received for it */
+ sta->flags |= WLAN_STA_PENDING_POLL;
+ prism2_send_mgmt(local->dev, IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_DATA, NULL, 0,
+ sta->addr, ap->tx_callback_poll);
+ } else {
+ int deauth = sta->timeout_next == STA_DEAUTH;
+ u16 resp;
+ PDEBUG(DEBUG_AP, "%s: sending %s info to STA " MACSTR
+ "(last=%lu, jiffies=%lu)\n",
+ local->dev->name,
+ deauth ? "deauthentication" : "disassociation",
+ MAC2STR(sta->addr), sta->last_rx, jiffies);
+
+ resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID :
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+ prism2_send_mgmt(local->dev, IEEE80211_FTYPE_MGMT |
+ (deauth ? IEEE80211_STYPE_DEAUTH :
+ IEEE80211_STYPE_DISASSOC),
+ (char *) &resp, 2, sta->addr, 0);
+ }
+
+ if (sta->timeout_next == STA_DEAUTH) {
+ if (sta->flags & WLAN_STA_PERM) {
+ PDEBUG(DEBUG_AP, "%s: STA " MACSTR " would have been "
+ "removed, but it has 'perm' flag\n",
+ local->dev->name, MAC2STR(sta->addr));
+ } else
+ ap_free_sta(ap, sta);
+ return;
+ }
+
+ if (sta->timeout_next == STA_NULLFUNC) {
+ sta->timeout_next = STA_DISASSOC;
+ sta->timer.expires = jiffies + AP_DISASSOC_DELAY;
+ } else {
+ sta->timeout_next = STA_DEAUTH;
+ sta->timer.expires = jiffies + AP_DEAUTH_DELAY;
+ }
+
+ add_timer(&sta->timer);
+}
+
+
+void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap,
+ int resend)
+{
+ u8 addr[ETH_ALEN];
+ u16 resp;
+ int i;
+
+ PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name);
+ memset(addr, 0xff, ETH_ALEN);
+
+ resp = __constant_cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+ /* deauth message sent; try to resend it few times; the message is
+ * broadcast, so it may be delayed until next DTIM; there is not much
+ * else we can do at this point since the driver is going to be shut
+ * down */
+ for (i = 0; i < 5; i++) {
+ prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_DEAUTH,
+ (char *) &resp, 2, addr, 0);
+
+ if (!resend || ap->num_sta <= 0)
+ return;
+
+ mdelay(50);
+ }
+}
+
+
+static int ap_control_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ struct ap_data *ap = (struct ap_data *) data;
+ char *policy_txt;
+ struct list_head *ptr;
+ struct mac_entry *entry;
+
+ if (off != 0) {
+ *eof = 1;
+ return 0;
+ }
+
+ switch (ap->mac_restrictions.policy) {
+ case MAC_POLICY_OPEN:
+ policy_txt = "open";
+ break;
+ case MAC_POLICY_ALLOW:
+ policy_txt = "allow";
+ break;
+ case MAC_POLICY_DENY:
+ policy_txt = "deny";
+ break;
+ default:
+ policy_txt = "unknown";
+ break;
+ };
+ p += sprintf(p, "MAC policy: %s\n", policy_txt);
+ p += sprintf(p, "MAC entries: %u\n", ap->mac_restrictions.entries);
+ p += sprintf(p, "MAC list:\n");
+ spin_lock_bh(&ap->mac_restrictions.lock);
+ for (ptr = ap->mac_restrictions.mac_list.next;
+ ptr != &ap->mac_restrictions.mac_list; ptr = ptr->next) {
+ if (p - page > PAGE_SIZE - 80) {
+ p += sprintf(p, "All entries did not fit one page.\n");
+ break;
+ }
+
+ entry = list_entry(ptr, struct mac_entry, list);
+ p += sprintf(p, MACSTR "\n", MAC2STR(entry->addr));
+ }
+ spin_unlock_bh(&ap->mac_restrictions.lock);
+
+ return (p - page);
+}
+
+
+static int ap_control_add_mac(struct mac_restrictions *mac_restrictions,
+ u8 *mac)
+{
+ struct mac_entry *entry;
+
+ entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL);
+ if (entry == NULL)
+ return -1;
+
+ memcpy(entry->addr, mac, ETH_ALEN);
+
+ spin_lock_bh(&mac_restrictions->lock);
+ list_add_tail(&entry->list, &mac_restrictions->mac_list);
+ mac_restrictions->entries++;
+ spin_unlock_bh(&mac_restrictions->lock);
+
+ return 0;
+}
+
+
+static int ap_control_del_mac(struct mac_restrictions *mac_restrictions,
+ u8 *mac)
+{
+ struct list_head *ptr;
+ struct mac_entry *entry;
+
+ spin_lock_bh(&mac_restrictions->lock);
+ for (ptr = mac_restrictions->mac_list.next;
+ ptr != &mac_restrictions->mac_list; ptr = ptr->next) {
+ entry = list_entry(ptr, struct mac_entry, list);
+
+ if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
+ list_del(ptr);
+ kfree(entry);
+ mac_restrictions->entries--;
+ spin_unlock_bh(&mac_restrictions->lock);
+ return 0;
+ }
+ }
+ spin_unlock_bh(&mac_restrictions->lock);
+ return -1;
+}
+
+
+static int ap_control_mac_deny(struct mac_restrictions *mac_restrictions,
+ u8 *mac)
+{
+ struct list_head *ptr;
+ struct mac_entry *entry;
+ int found = 0;
+
+ if (mac_restrictions->policy == MAC_POLICY_OPEN)
+ return 0;
+
+ spin_lock_bh(&mac_restrictions->lock);
+ for (ptr = mac_restrictions->mac_list.next;
+ ptr != &mac_restrictions->mac_list; ptr = ptr->next) {
+ entry = list_entry(ptr, struct mac_entry, list);
+
+ if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_bh(&mac_restrictions->lock);
+
+ if (mac_restrictions->policy == MAC_POLICY_ALLOW)
+ return !found;
+ else
+ return found;
+}
+
+
+static void ap_control_flush_macs(struct mac_restrictions *mac_restrictions)
+{
+ struct list_head *ptr, *n;
+ struct mac_entry *entry;
+
+ if (mac_restrictions->entries == 0)
+ return;
+
+ spin_lock_bh(&mac_restrictions->lock);
+ for (ptr = mac_restrictions->mac_list.next, n = ptr->next;
+ ptr != &mac_restrictions->mac_list;
+ ptr = n, n = ptr->next) {
+ entry = list_entry(ptr, struct mac_entry, list);
+ list_del(ptr);
+ kfree(entry);
+ }
+ mac_restrictions->entries = 0;
+ spin_unlock_bh(&mac_restrictions->lock);
+}
+
+
+static int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev,
+ u8 *mac)
+{
+ struct sta_info *sta;
+ u16 resp;
+
+ spin_lock_bh(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, mac);
+ if (sta) {
+ ap_sta_hash_del(ap, sta);
+ list_del(&sta->list);
+ }
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ if (!sta)
+ return -EINVAL;
+
+ resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+ prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH,
+ (char *) &resp, 2, sta->addr, 0);
+
+ if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+ hostap_event_expired_sta(dev, sta);
+
+ ap_free_sta(ap, sta);
+
+ return 0;
+}
+
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+static void ap_control_kickall(struct ap_data *ap)
+{
+ struct list_head *ptr, *n;
+ struct sta_info *sta;
+
+ spin_lock_bh(&ap->sta_table_lock);
+ for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list;
+ ptr = n, n = ptr->next) {
+ sta = list_entry(ptr, struct sta_info, list);
+ ap_sta_hash_del(ap, sta);
+ list_del(&sta->list);
+ if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+ hostap_event_expired_sta(sta->local->dev, sta);
+ ap_free_sta(ap, sta);
+ }
+ spin_unlock_bh(&ap->sta_table_lock);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+#define PROC_LIMIT (PAGE_SIZE - 80)
+
+static int prism2_ap_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ struct ap_data *ap = (struct ap_data *) data;
+ struct list_head *ptr;
+ int i;
+
+ if (off > PROC_LIMIT) {
+ *eof = 1;
+ return 0;
+ }
+
+ p += sprintf(p, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n");
+ spin_lock_bh(&ap->sta_table_lock);
+ for (ptr = ap->sta_list.next; ptr != &ap->sta_list; ptr = ptr->next) {
+ struct sta_info *sta = (struct sta_info *) ptr;
+
+ if (!sta->ap)
+ continue;
+
+ p += sprintf(p, MACSTR " %d %d %d %d '", MAC2STR(sta->addr),
+ sta->u.ap.channel, sta->last_rx_signal,
+ sta->last_rx_silence, sta->last_rx_rate);
+ for (i = 0; i < sta->u.ap.ssid_len; i++)
+ p += sprintf(p, ((sta->u.ap.ssid[i] >= 32 &&
+ sta->u.ap.ssid[i] < 127) ?
+ "%c" : "<%02x>"),
+ sta->u.ap.ssid[i]);
+ p += sprintf(p, "'");
+ if (sta->capability & WLAN_CAPABILITY_ESS)
+ p += sprintf(p, " [ESS]");
+ if (sta->capability & WLAN_CAPABILITY_IBSS)
+ p += sprintf(p, " [IBSS]");
+ if (sta->capability & WLAN_CAPABILITY_PRIVACY)
+ p += sprintf(p, " [WEP]");
+ p += sprintf(p, "\n");
+
+ if ((p - page) > PROC_LIMIT) {
+ printk(KERN_DEBUG "hostap: ap proc did not fit\n");
+ break;
+ }
+ }
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ if ((p - page) <= off) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+
+ return (p - page - off);
+}
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver)
+{
+ if (!ap)
+ return;
+
+ if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) {
+ PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - "
+ "firmware upgrade recommended\n");
+ ap->nullfunc_ack = 1;
+ } else
+ ap->nullfunc_ack = 0;
+
+ if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) {
+ printk(KERN_WARNING "%s: Warning: secondary station firmware "
+ "version 1.4.2 does not seem to work in Host AP mode\n",
+ ap->local->dev->name);
+ }
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data)
+{
+ struct ap_data *ap = data;
+ u16 fc;
+ struct ieee80211_hdr *hdr;
+
+ if (!ap->local->hostapd || !ap->local->apdev) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ /* Pass the TX callback frame to the hostapd; use 802.11 header version
+ * 1 to indicate failure (no ACK) and 2 success (frame ACKed) */
+
+ fc &= ~IEEE80211_FCTL_VERS;
+ fc |= ok ? BIT(1) : BIT(0);
+ hdr->frame_ctl = cpu_to_le16(fc);
+
+ skb->dev = ap->local->apdev;
+ skb_pull(skb, hostap_80211_get_hdrlen(fc));
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = __constant_htons(ETH_P_802_2);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ netif_rx(skb);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+/* Called only as a tasklet (software IRQ) */
+static void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data)
+{
+ struct ap_data *ap = data;
+ struct net_device *dev = ap->local->dev;
+ struct ieee80211_hdr *hdr;
+ u16 fc, *pos, auth_alg, auth_transaction, status;
+ struct sta_info *sta = NULL;
+ char *txt = NULL;
+
+ if (ap->local->hostapd) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ if (WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_MGMT ||
+ WLAN_FC_GET_STYPE(fc) != IEEE80211_STYPE_AUTH ||
+ skb->len < IEEE80211_MGMT_HDR_LEN + 6) {
+ printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid "
+ "frame\n", dev->name);
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+ auth_alg = le16_to_cpu(*pos++);
+ auth_transaction = le16_to_cpu(*pos++);
+ status = le16_to_cpu(*pos++);
+
+ if (!ok) {
+ txt = "frame was not ACKed";
+ goto done;
+ }
+
+ spin_lock(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, hdr->addr1);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock(&ap->sta_table_lock);
+
+ if (!sta) {
+ txt = "STA not found";
+ goto done;
+ }
+
+ if (status == WLAN_STATUS_SUCCESS &&
+ ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
+ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
+ txt = "STA authenticated";
+ sta->flags |= WLAN_STA_AUTH;
+ sta->last_auth = jiffies;
+ } else if (status != WLAN_STATUS_SUCCESS)
+ txt = "authentication failed";
+
+ done:
+ if (sta)
+ atomic_dec(&sta->users);
+ if (txt) {
+ PDEBUG(DEBUG_AP, "%s: " MACSTR " auth_cb - alg=%d trans#=%d "
+ "status=%d - %s\n",
+ dev->name, MAC2STR(hdr->addr1), auth_alg,
+ auth_transaction, status, txt);
+ }
+ dev_kfree_skb(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data)
+{
+ struct ap_data *ap = data;
+ struct net_device *dev = ap->local->dev;
+ struct ieee80211_hdr *hdr;
+ u16 fc, *pos, status;
+ struct sta_info *sta = NULL;
+ char *txt = NULL;
+
+ if (ap->local->hostapd) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ if (WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_MGMT ||
+ (WLAN_FC_GET_STYPE(fc) != IEEE80211_STYPE_ASSOC_RESP &&
+ WLAN_FC_GET_STYPE(fc) != IEEE80211_STYPE_REASSOC_RESP) ||
+ skb->len < IEEE80211_MGMT_HDR_LEN + 4) {
+ printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid "
+ "frame\n", dev->name);
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if (!ok) {
+ txt = "frame was not ACKed";
+ goto done;
+ }
+
+ spin_lock(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, hdr->addr1);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock(&ap->sta_table_lock);
+
+ if (!sta) {
+ txt = "STA not found";
+ goto done;
+ }
+
+ pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+ pos++;
+ status = le16_to_cpu(*pos++);
+ if (status == WLAN_STATUS_SUCCESS) {
+ if (!(sta->flags & WLAN_STA_ASSOC))
+ hostap_event_new_sta(dev, sta);
+ txt = "STA associated";
+ sta->flags |= WLAN_STA_ASSOC;
+ sta->last_assoc = jiffies;
+ } else
+ txt = "association failed";
+
+ done:
+ if (sta)
+ atomic_dec(&sta->users);
+ if (txt) {
+ PDEBUG(DEBUG_AP, "%s: " MACSTR " assoc_cb - %s\n",
+ dev->name, MAC2STR(hdr->addr1), txt);
+ }
+ dev_kfree_skb(skb);
+}
+
+/* Called only as a tasklet (software IRQ); TX callback for poll frames used
+ * in verifying whether the STA is still present. */
+static void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data)
+{
+ struct ap_data *ap = data;
+ struct ieee80211_hdr *hdr;
+ struct sta_info *sta;
+
+ if (skb->len < 24)
+ goto fail;
+ hdr = (struct ieee80211_hdr *) skb->data;
+ if (ok) {
+ spin_lock(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, hdr->addr1);
+ if (sta)
+ sta->flags &= ~WLAN_STA_PENDING_POLL;
+ spin_unlock(&ap->sta_table_lock);
+ } else {
+ PDEBUG(DEBUG_AP, "%s: STA " MACSTR " did not ACK activity "
+ "poll frame\n", ap->local->dev->name,
+ MAC2STR(hdr->addr1));
+ }
+
+ fail:
+ dev_kfree_skb(skb);
+}
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+void hostap_init_data(local_info_t *local)
+{
+ struct ap_data *ap = local->ap;
+
+ if (ap == NULL) {
+ printk(KERN_WARNING "hostap_init_data: ap == NULL\n");
+ return;
+ }
+ memset(ap, 0, sizeof(struct ap_data));
+ ap->local = local;
+
+ ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx);
+ ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx);
+ ap->max_inactivity =
+ GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ;
+ ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx);
+
+ spin_lock_init(&ap->sta_table_lock);
+ INIT_LIST_HEAD(&ap->sta_list);
+
+ /* Initialize task queue structure for AP management */
+ INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue, ap);
+
+ ap->tx_callback_idx =
+ hostap_tx_callback_register(local, hostap_ap_tx_cb, ap);
+ if (ap->tx_callback_idx == 0)
+ printk(KERN_WARNING "%s: failed to register TX callback for "
+ "AP\n", local->dev->name);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue, local);
+
+ ap->tx_callback_auth =
+ hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap);
+ ap->tx_callback_assoc =
+ hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap);
+ ap->tx_callback_poll =
+ hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap);
+ if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 ||
+ ap->tx_callback_poll == 0)
+ printk(KERN_WARNING "%s: failed to register TX callback for "
+ "AP\n", local->dev->name);
+
+ spin_lock_init(&ap->mac_restrictions.lock);
+ INIT_LIST_HEAD(&ap->mac_restrictions.mac_list);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ ap->initialized = 1;
+}
+
+
+void hostap_init_ap_proc(local_info_t *local)
+{
+ struct ap_data *ap = local->ap;
+
+ ap->proc = local->proc;
+ if (ap->proc == NULL)
+ return;
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+ create_proc_read_entry("ap_debug", 0, ap->proc,
+ ap_debug_proc_read, ap);
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ create_proc_read_entry("ap_control", 0, ap->proc,
+ ap_control_proc_read, ap);
+ create_proc_read_entry("ap", 0, ap->proc,
+ prism2_ap_proc_read, ap);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+}
+
+
+void hostap_free_data(struct ap_data *ap)
+{
+ struct list_head *n, *ptr;
+
+ if (ap == NULL || !ap->initialized) {
+ printk(KERN_DEBUG "hostap_free_data: ap has not yet been "
+ "initialized - skip resource freeing\n");
+ return;
+ }
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ if (ap->crypt)
+ ap->crypt->deinit(ap->crypt_priv);
+ ap->crypt = ap->crypt_priv = NULL;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ list_for_each_safe(ptr, n, &ap->sta_list) {
+ struct sta_info *sta = list_entry(ptr, struct sta_info, list);
+ ap_sta_hash_del(ap, sta);
+ list_del(&sta->list);
+ if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+ hostap_event_expired_sta(sta->local->dev, sta);
+ ap_free_sta(ap, sta);
+ }
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+ if (ap->proc != NULL) {
+ remove_proc_entry("ap_debug", ap->proc);
+ }
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ if (ap->proc != NULL) {
+ remove_proc_entry("ap", ap->proc);
+ remove_proc_entry("ap_control", ap->proc);
+ }
+ ap_control_flush_macs(&ap->mac_restrictions);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ ap->initialized = 0;
+}
+
+
+/* caller should have mutex for AP STA list handling */
+static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta)
+{
+ struct sta_info *s;
+
+ s = ap->sta_hash[STA_HASH(sta)];
+ while (s != NULL && memcmp(s->addr, sta, ETH_ALEN) != 0)
+ s = s->hnext;
+ return s;
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+/* Called from timer handler and from scheduled AP queue handlers */
+static void prism2_send_mgmt(struct net_device *dev,
+ u16 type_subtype, char *body,
+ int body_len, u8 *addr, u16 tx_cb_idx)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+ struct sk_buff *skb;
+ struct hostap_skb_tx_data *meta;
+ int hdrlen;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ dev = local->dev; /* always use master radio device */
+ iface = netdev_priv(dev);
+
+ if (!(dev->flags & IFF_UP)) {
+ PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - "
+ "cannot send frame\n", dev->name);
+ return;
+ }
+
+ skb = dev_alloc_skb(sizeof(*hdr) + body_len);
+ if (skb == NULL) {
+ PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate "
+ "skb\n", dev->name);
+ return;
+ }
+
+ fc = type_subtype;
+ hdrlen = hostap_80211_get_hdrlen(fc);
+ hdr = (struct ieee80211_hdr *) skb_put(skb, hdrlen);
+ if (body)
+ memcpy(skb_put(skb, body_len), body, body_len);
+
+ memset(hdr, 0, hdrlen);
+
+ /* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11
+ * tx_control instead of using local->tx_control */
+
+
+ memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */
+ if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) {
+ fc |= IEEE80211_FCTL_FROMDS;
+ memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */
+ memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */
+ } else if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_CTL) {
+ /* control:ACK does not have addr2 or addr3 */
+ memset(hdr->addr2, 0, ETH_ALEN);
+ memset(hdr->addr3, 0, ETH_ALEN);
+ } else {
+ memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */
+ memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */
+ }
+
+ hdr->frame_ctl = cpu_to_le16(fc);
+
+ meta = (struct hostap_skb_tx_data *) skb->cb;
+ memset(meta, 0, sizeof(*meta));
+ meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
+ meta->iface = iface;
+ meta->tx_cb_idx = tx_cb_idx;
+
+ skb->dev = dev;
+ skb->mac.raw = skb->nh.raw = skb->data;
+ dev_queue_xmit(skb);
+}
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+static int prism2_sta_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ struct sta_info *sta = (struct sta_info *) data;
+ int i;
+
+ /* FIX: possible race condition.. the STA data could have just expired,
+ * but proc entry was still here so that the read could have started;
+ * some locking should be done here.. */
+
+ if (off != 0) {
+ *eof = 1;
+ return 0;
+ }
+
+ p += sprintf(p, "%s=" MACSTR "\nusers=%d\naid=%d\n"
+ "flags=0x%04x%s%s%s%s%s%s%s\n"
+ "capability=0x%02x\nlisten_interval=%d\nsupported_rates=",
+ sta->ap ? "AP" : "STA",
+ MAC2STR(sta->addr), atomic_read(&sta->users), sta->aid,
+ sta->flags,
+ sta->flags & WLAN_STA_AUTH ? " AUTH" : "",
+ sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "",
+ sta->flags & WLAN_STA_PS ? " PS" : "",
+ sta->flags & WLAN_STA_TIM ? " TIM" : "",
+ sta->flags & WLAN_STA_PERM ? " PERM" : "",
+ sta->flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED" : "",
+ sta->flags & WLAN_STA_PENDING_POLL ? " POLL" : "",
+ sta->capability, sta->listen_interval);
+ /* supported_rates: 500 kbit/s units with msb ignored */
+ for (i = 0; i < sizeof(sta->supported_rates); i++)
+ if (sta->supported_rates[i] != 0)
+ p += sprintf(p, "%d%sMbps ",
+ (sta->supported_rates[i] & 0x7f) / 2,
+ sta->supported_rates[i] & 1 ? ".5" : "");
+ p += sprintf(p, "\njiffies=%lu\nlast_auth=%lu\nlast_assoc=%lu\n"
+ "last_rx=%lu\nlast_tx=%lu\nrx_packets=%lu\n"
+ "tx_packets=%lu\n"
+ "rx_bytes=%lu\ntx_bytes=%lu\nbuffer_count=%d\n"
+ "last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps\n"
+ "tx_rate=%d\ntx[1M]=%d\ntx[2M]=%d\ntx[5.5M]=%d\n"
+ "tx[11M]=%d\n"
+ "rx[1M]=%d\nrx[2M]=%d\nrx[5.5M]=%d\nrx[11M]=%d\n",
+ jiffies, sta->last_auth, sta->last_assoc, sta->last_rx,
+ sta->last_tx,
+ sta->rx_packets, sta->tx_packets, sta->rx_bytes,
+ sta->tx_bytes, skb_queue_len(&sta->tx_buf),
+ sta->last_rx_silence,
+ sta->last_rx_signal, sta->last_rx_rate / 10,
+ sta->last_rx_rate % 10 ? ".5" : "",
+ sta->tx_rate, sta->tx_count[0], sta->tx_count[1],
+ sta->tx_count[2], sta->tx_count[3], sta->rx_count[0],
+ sta->rx_count[1], sta->rx_count[2], sta->rx_count[3]);
+ if (sta->crypt && sta->crypt->ops && sta->crypt->ops->print_stats)
+ p = sta->crypt->ops->print_stats(p, sta->crypt->priv);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ if (sta->ap) {
+ if (sta->u.ap.channel >= 0)
+ p += sprintf(p, "channel=%d\n", sta->u.ap.channel);
+ p += sprintf(p, "ssid=");
+ for (i = 0; i < sta->u.ap.ssid_len; i++)
+ p += sprintf(p, ((sta->u.ap.ssid[i] >= 32 &&
+ sta->u.ap.ssid[i] < 127) ?
+ "%c" : "<%02x>"),
+ sta->u.ap.ssid[i]);
+ p += sprintf(p, "\n");
+ }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ return (p - page);
+}
+
+
+static void handle_add_proc_queue(void *data)
+{
+ struct ap_data *ap = (struct ap_data *) data;
+ struct sta_info *sta;
+ char name[20];
+ struct add_sta_proc_data *entry, *prev;
+
+ entry = ap->add_sta_proc_entries;
+ ap->add_sta_proc_entries = NULL;
+
+ while (entry) {
+ spin_lock_bh(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, entry->addr);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ if (sta) {
+ sprintf(name, MACSTR, MAC2STR(sta->addr));
+ sta->proc = create_proc_read_entry(
+ name, 0, ap->proc,
+ prism2_sta_proc_read, sta);
+
+ atomic_dec(&sta->users);
+ }
+
+ prev = entry;
+ entry = entry->next;
+ kfree(prev);
+ }
+}
+
+
+static struct sta_info * ap_add_sta(struct ap_data *ap, u8 *addr)
+{
+ struct sta_info *sta;
+
+ sta = (struct sta_info *)
+ kmalloc(sizeof(struct sta_info), GFP_ATOMIC);
+ if (sta == NULL) {
+ PDEBUG(DEBUG_AP, "AP: kmalloc failed\n");
+ return NULL;
+ }
+
+ /* initialize STA info data */
+ memset(sta, 0, sizeof(struct sta_info));
+ sta->local = ap->local;
+ skb_queue_head_init(&sta->tx_buf);
+ memcpy(sta->addr, addr, ETH_ALEN);
+
+ atomic_inc(&sta->users);
+ spin_lock_bh(&ap->sta_table_lock);
+ list_add(&sta->list, &ap->sta_list);
+ ap->num_sta++;
+ ap_sta_hash_add(ap, sta);
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ if (ap->proc) {
+ struct add_sta_proc_data *entry;
+ /* schedule a non-interrupt context process to add a procfs
+ * entry for the STA since procfs code use GFP_KERNEL */
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (entry) {
+ memcpy(entry->addr, sta->addr, ETH_ALEN);
+ entry->next = ap->add_sta_proc_entries;
+ ap->add_sta_proc_entries = entry;
+ schedule_work(&ap->add_sta_proc_queue);
+ } else
+ printk(KERN_DEBUG "Failed to add STA proc data\n");
+ }
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ init_timer(&sta->timer);
+ sta->timer.expires = jiffies + ap->max_inactivity;
+ sta->timer.data = (unsigned long) sta;
+ sta->timer.function = ap_handle_timer;
+ if (!ap->local->hostapd)
+ add_timer(&sta->timer);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ return sta;
+}
+
+
+static int ap_tx_rate_ok(int rateidx, struct sta_info *sta,
+ local_info_t *local)
+{
+ if (rateidx > sta->tx_max_rate ||
+ !(sta->tx_supp_rates & (1 << rateidx)))
+ return 0;
+
+ if (local->tx_rate_control != 0 &&
+ !(local->tx_rate_control & (1 << rateidx)))
+ return 0;
+
+ return 1;
+}
+
+
+static void prism2_check_tx_rates(struct sta_info *sta)
+{
+ int i;
+
+ sta->tx_supp_rates = 0;
+ for (i = 0; i < sizeof(sta->supported_rates); i++) {
+ if ((sta->supported_rates[i] & 0x7f) == 2)
+ sta->tx_supp_rates |= WLAN_RATE_1M;
+ if ((sta->supported_rates[i] & 0x7f) == 4)
+ sta->tx_supp_rates |= WLAN_RATE_2M;
+ if ((sta->supported_rates[i] & 0x7f) == 11)
+ sta->tx_supp_rates |= WLAN_RATE_5M5;
+ if ((sta->supported_rates[i] & 0x7f) == 22)
+ sta->tx_supp_rates |= WLAN_RATE_11M;
+ }
+ sta->tx_max_rate = sta->tx_rate = sta->tx_rate_idx = 0;
+ if (sta->tx_supp_rates & WLAN_RATE_1M) {
+ sta->tx_max_rate = 0;
+ if (ap_tx_rate_ok(0, sta, sta->local)) {
+ sta->tx_rate = 10;
+ sta->tx_rate_idx = 0;
+ }
+ }
+ if (sta->tx_supp_rates & WLAN_RATE_2M) {
+ sta->tx_max_rate = 1;
+ if (ap_tx_rate_ok(1, sta, sta->local)) {
+ sta->tx_rate = 20;
+ sta->tx_rate_idx = 1;
+ }
+ }
+ if (sta->tx_supp_rates & WLAN_RATE_5M5) {
+ sta->tx_max_rate = 2;
+ if (ap_tx_rate_ok(2, sta, sta->local)) {
+ sta->tx_rate = 55;
+ sta->tx_rate_idx = 2;
+ }
+ }
+ if (sta->tx_supp_rates & WLAN_RATE_11M) {
+ sta->tx_max_rate = 3;
+ if (ap_tx_rate_ok(3, sta, sta->local)) {
+ sta->tx_rate = 110;
+ sta->tx_rate_idx = 3;
+ }
+ }
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+static void ap_crypt_init(struct ap_data *ap)
+{
+ ap->crypt = ieee80211_get_crypto_ops("WEP");
+
+ if (ap->crypt) {
+ if (ap->crypt->init) {
+ ap->crypt_priv = ap->crypt->init(0);
+ if (ap->crypt_priv == NULL)
+ ap->crypt = NULL;
+ else {
+ u8 key[WEP_KEY_LEN];
+ get_random_bytes(key, WEP_KEY_LEN);
+ ap->crypt->set_key(key, WEP_KEY_LEN, NULL,
+ ap->crypt_priv);
+ }
+ }
+ }
+
+ if (ap->crypt == NULL) {
+ printk(KERN_WARNING "AP could not initialize WEP: load module "
+ "ieee80211_crypt_wep.ko\n");
+ }
+}
+
+
+/* Generate challenge data for shared key authentication. IEEE 802.11 specifies
+ * that WEP algorithm is used for generating challange. This should be unique,
+ * but otherwise there is not really need for randomness etc. Initialize WEP
+ * with pseudo random key and then use increasing IV to get unique challenge
+ * streams.
+ *
+ * Called only as a scheduled task for pending AP frames.
+ */
+static char * ap_auth_make_challenge(struct ap_data *ap)
+{
+ char *tmpbuf;
+ struct sk_buff *skb;
+
+ if (ap->crypt == NULL) {
+ ap_crypt_init(ap);
+ if (ap->crypt == NULL)
+ return NULL;
+ }
+
+ tmpbuf = (char *) kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC);
+ if (tmpbuf == NULL) {
+ PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n");
+ return NULL;
+ }
+
+ skb = dev_alloc_skb(WLAN_AUTH_CHALLENGE_LEN +
+ ap->crypt->extra_prefix_len +
+ ap->crypt->extra_postfix_len);
+ if (skb == NULL) {
+ kfree(tmpbuf);
+ return NULL;
+ }
+
+ skb_reserve(skb, ap->crypt->extra_prefix_len);
+ memset(skb_put(skb, WLAN_AUTH_CHALLENGE_LEN), 0,
+ WLAN_AUTH_CHALLENGE_LEN);
+ if (ap->crypt->encrypt_mpdu(skb, 0, ap->crypt_priv)) {
+ dev_kfree_skb(skb);
+ kfree(tmpbuf);
+ return NULL;
+ }
+
+ memcpy(tmpbuf, skb->data + ap->crypt->extra_prefix_len,
+ WLAN_AUTH_CHALLENGE_LEN);
+ dev_kfree_skb(skb);
+
+ return tmpbuf;
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_authen(local_info_t *local, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ struct net_device *dev = local->dev;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ size_t hdrlen;
+ struct ap_data *ap = local->ap;
+ char body[8 + WLAN_AUTH_CHALLENGE_LEN], *challenge = NULL;
+ int len, olen;
+ u16 auth_alg, auth_transaction, status_code, *pos;
+ u16 resp = WLAN_STATUS_SUCCESS, fc;
+ struct sta_info *sta = NULL;
+ struct ieee80211_crypt_data *crypt;
+ char *txt = "";
+
+ len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ hdrlen = hostap_80211_get_hdrlen(fc);
+
+ if (len < 6) {
+ PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload "
+ "(len=%d) from " MACSTR "\n", dev->name, len,
+ MAC2STR(hdr->addr2));
+ return;
+ }
+
+ spin_lock_bh(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr2);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock_bh(&local->ap->sta_table_lock);
+
+ if (sta && sta->crypt)
+ crypt = sta->crypt;
+ else {
+ int idx = 0;
+ if (skb->len >= hdrlen + 3)
+ idx = skb->data[hdrlen + 3] >> 6;
+ crypt = local->crypt[idx];
+ }
+
+ pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+ auth_alg = __le16_to_cpu(*pos);
+ pos++;
+ auth_transaction = __le16_to_cpu(*pos);
+ pos++;
+ status_code = __le16_to_cpu(*pos);
+ pos++;
+
+ if (memcmp(dev->dev_addr, hdr->addr2, ETH_ALEN) == 0 ||
+ ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) {
+ txt = "authentication denied";
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (((local->auth_algs & PRISM2_AUTH_OPEN) &&
+ auth_alg == WLAN_AUTH_OPEN) ||
+ ((local->auth_algs & PRISM2_AUTH_SHARED_KEY) &&
+ crypt && auth_alg == WLAN_AUTH_SHARED_KEY)) {
+ } else {
+ txt = "unsupported algorithm";
+ resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ goto fail;
+ }
+
+ if (len >= 8) {
+ u8 *u = (u8 *) pos;
+ if (*u == WLAN_EID_CHALLENGE) {
+ if (*(u + 1) != WLAN_AUTH_CHALLENGE_LEN) {
+ txt = "invalid challenge len";
+ resp = WLAN_STATUS_CHALLENGE_FAIL;
+ goto fail;
+ }
+ if (len - 8 < WLAN_AUTH_CHALLENGE_LEN) {
+ txt = "challenge underflow";
+ resp = WLAN_STATUS_CHALLENGE_FAIL;
+ goto fail;
+ }
+ challenge = (char *) (u + 2);
+ }
+ }
+
+ if (sta && sta->ap) {
+ if (time_after(jiffies, sta->u.ap.last_beacon +
+ (10 * sta->listen_interval * HZ) / 1024)) {
+ PDEBUG(DEBUG_AP, "%s: no beacons received for a while,"
+ " assuming AP " MACSTR " is now STA\n",
+ dev->name, MAC2STR(sta->addr));
+ sta->ap = 0;
+ sta->flags = 0;
+ sta->u.sta.challenge = NULL;
+ } else {
+ txt = "AP trying to authenticate?";
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ }
+
+ if ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) ||
+ (auth_alg == WLAN_AUTH_SHARED_KEY &&
+ (auth_transaction == 1 ||
+ (auth_transaction == 3 && sta != NULL &&
+ sta->u.sta.challenge != NULL)))) {
+ } else {
+ txt = "unknown authentication transaction number";
+ resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ goto fail;
+ }
+
+ if (sta == NULL) {
+ txt = "new STA";
+
+ if (local->ap->num_sta >= MAX_STA_COUNT) {
+ /* FIX: might try to remove some old STAs first? */
+ txt = "no more room for new STAs";
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ sta = ap_add_sta(local->ap, hdr->addr2);
+ if (sta == NULL) {
+ txt = "ap_add_sta failed";
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ }
+
+ switch (auth_alg) {
+ case WLAN_AUTH_OPEN:
+ txt = "authOK";
+ /* IEEE 802.11 standard is not completely clear about
+ * whether STA is considered authenticated after
+ * authentication OK frame has been send or after it
+ * has been ACKed. In order to reduce interoperability
+ * issues, mark the STA authenticated before ACK. */
+ sta->flags |= WLAN_STA_AUTH;
+ break;
+
+ case WLAN_AUTH_SHARED_KEY:
+ if (auth_transaction == 1) {
+ if (sta->u.sta.challenge == NULL) {
+ sta->u.sta.challenge =
+ ap_auth_make_challenge(local->ap);
+ if (sta->u.sta.challenge == NULL) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ }
+ } else {
+ if (sta->u.sta.challenge == NULL ||
+ challenge == NULL ||
+ memcmp(sta->u.sta.challenge, challenge,
+ WLAN_AUTH_CHALLENGE_LEN) != 0 ||
+ !(fc & IEEE80211_FCTL_PROTECTED)) {
+ txt = "challenge response incorrect";
+ resp = WLAN_STATUS_CHALLENGE_FAIL;
+ goto fail;
+ }
+
+ txt = "challenge OK - authOK";
+ /* IEEE 802.11 standard is not completely clear about
+ * whether STA is considered authenticated after
+ * authentication OK frame has been send or after it
+ * has been ACKed. In order to reduce interoperability
+ * issues, mark the STA authenticated before ACK. */
+ sta->flags |= WLAN_STA_AUTH;
+ kfree(sta->u.sta.challenge);
+ sta->u.sta.challenge = NULL;
+ }
+ break;
+ }
+
+ fail:
+ pos = (u16 *) body;
+ *pos = cpu_to_le16(auth_alg);
+ pos++;
+ *pos = cpu_to_le16(auth_transaction + 1);
+ pos++;
+ *pos = cpu_to_le16(resp); /* status_code */
+ pos++;
+ olen = 6;
+
+ if (resp == WLAN_STATUS_SUCCESS && sta != NULL &&
+ sta->u.sta.challenge != NULL &&
+ auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 1) {
+ u8 *tmp = (u8 *) pos;
+ *tmp++ = WLAN_EID_CHALLENGE;
+ *tmp++ = WLAN_AUTH_CHALLENGE_LEN;
+ pos++;
+ memcpy(pos, sta->u.sta.challenge, WLAN_AUTH_CHALLENGE_LEN);
+ olen += 2 + WLAN_AUTH_CHALLENGE_LEN;
+ }
+
+ prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH,
+ body, olen, hdr->addr2, ap->tx_callback_auth);
+
+ if (sta) {
+ sta->last_rx = jiffies;
+ atomic_dec(&sta->users);
+ }
+
+ if (resp) {
+ PDEBUG(DEBUG_AP, "%s: " MACSTR " auth (alg=%d trans#=%d "
+ "stat=%d len=%d fc=%04x) ==> %d (%s)\n",
+ dev->name, MAC2STR(hdr->addr2), auth_alg,
+ auth_transaction, status_code, len, fc, resp, txt);
+ }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_assoc(local_info_t *local, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats, int reassoc)
+{
+ struct net_device *dev = local->dev;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ char body[12], *p, *lpos;
+ int len, left;
+ u16 *pos;
+ u16 resp = WLAN_STATUS_SUCCESS;
+ struct sta_info *sta = NULL;
+ int send_deauth = 0;
+ char *txt = "";
+ u8 prev_ap[ETH_ALEN];
+
+ left = len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+ if (len < (reassoc ? 10 : 4)) {
+ PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload "
+ "(len=%d, reassoc=%d) from " MACSTR "\n",
+ dev->name, len, reassoc, MAC2STR(hdr->addr2));
+ return;
+ }
+
+ spin_lock_bh(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr2);
+ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
+ spin_unlock_bh(&local->ap->sta_table_lock);
+ txt = "trying to associate before authentication";
+ send_deauth = 1;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ sta = NULL; /* do not decrement sta->users */
+ goto fail;
+ }
+ atomic_inc(&sta->users);
+ spin_unlock_bh(&local->ap->sta_table_lock);
+
+ pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+ sta->capability = __le16_to_cpu(*pos);
+ pos++; left -= 2;
+ sta->listen_interval = __le16_to_cpu(*pos);
+ pos++; left -= 2;
+
+ if (reassoc) {
+ memcpy(prev_ap, pos, ETH_ALEN);
+ pos++; pos++; pos++; left -= 6;
+ } else
+ memset(prev_ap, 0, ETH_ALEN);
+
+ if (left >= 2) {
+ unsigned int ileft;
+ unsigned char *u = (unsigned char *) pos;
+
+ if (*u == WLAN_EID_SSID) {
+ u++; left--;
+ ileft = *u;
+ u++; left--;
+
+ if (ileft > left || ileft > MAX_SSID_LEN) {
+ txt = "SSID overflow";
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (ileft != strlen(local->essid) ||
+ memcmp(local->essid, u, ileft) != 0) {
+ txt = "not our SSID";
+ resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ goto fail;
+ }
+
+ u += ileft;
+ left -= ileft;
+ }
+
+ if (left >= 2 && *u == WLAN_EID_SUPP_RATES) {
+ u++; left--;
+ ileft = *u;
+ u++; left--;
+
+ if (ileft > left || ileft == 0 ||
+ ileft > WLAN_SUPP_RATES_MAX) {
+ txt = "SUPP_RATES len error";
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ memset(sta->supported_rates, 0,
+ sizeof(sta->supported_rates));
+ memcpy(sta->supported_rates, u, ileft);
+ prism2_check_tx_rates(sta);
+
+ u += ileft;
+ left -= ileft;
+ }
+
+ if (left > 0) {
+ PDEBUG(DEBUG_AP, "%s: assoc from " MACSTR " with extra"
+ " data (%d bytes) [",
+ dev->name, MAC2STR(hdr->addr2), left);
+ while (left > 0) {
+ PDEBUG2(DEBUG_AP, "<%02x>", *u);
+ u++; left--;
+ }
+ PDEBUG2(DEBUG_AP, "]\n");
+ }
+ } else {
+ txt = "frame underflow";
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ /* get a unique AID */
+ if (sta->aid > 0)
+ txt = "OK, old AID";
+ else {
+ spin_lock_bh(&local->ap->sta_table_lock);
+ for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++)
+ if (local->ap->sta_aid[sta->aid - 1] == NULL)
+ break;
+ if (sta->aid > MAX_AID_TABLE_SIZE) {
+ sta->aid = 0;
+ spin_unlock_bh(&local->ap->sta_table_lock);
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ txt = "no room for more AIDs";
+ } else {
+ local->ap->sta_aid[sta->aid - 1] = sta;
+ spin_unlock_bh(&local->ap->sta_table_lock);
+ txt = "OK, new AID";
+ }
+ }
+
+ fail:
+ pos = (u16 *) body;
+
+ if (send_deauth) {
+ *pos = __constant_cpu_to_le16(
+ WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH);
+ pos++;
+ } else {
+ /* FIX: CF-Pollable and CF-PollReq should be set to match the
+ * values in beacons/probe responses */
+ /* FIX: how about privacy and WEP? */
+ /* capability */
+ *pos = __constant_cpu_to_le16(WLAN_CAPABILITY_ESS);
+ pos++;
+
+ /* status_code */
+ *pos = __cpu_to_le16(resp);
+ pos++;
+
+ *pos = __cpu_to_le16((sta && sta->aid > 0 ? sta->aid : 0) |
+ BIT(14) | BIT(15)); /* AID */
+ pos++;
+
+ /* Supported rates (Information element) */
+ p = (char *) pos;
+ *p++ = WLAN_EID_SUPP_RATES;
+ lpos = p;
+ *p++ = 0; /* len */
+ if (local->tx_rate_control & WLAN_RATE_1M) {
+ *p++ = local->basic_rates & WLAN_RATE_1M ? 0x82 : 0x02;
+ (*lpos)++;
+ }
+ if (local->tx_rate_control & WLAN_RATE_2M) {
+ *p++ = local->basic_rates & WLAN_RATE_2M ? 0x84 : 0x04;
+ (*lpos)++;
+ }
+ if (local->tx_rate_control & WLAN_RATE_5M5) {
+ *p++ = local->basic_rates & WLAN_RATE_5M5 ?
+ 0x8b : 0x0b;
+ (*lpos)++;
+ }
+ if (local->tx_rate_control & WLAN_RATE_11M) {
+ *p++ = local->basic_rates & WLAN_RATE_11M ?
+ 0x96 : 0x16;
+ (*lpos)++;
+ }
+ pos = (u16 *) p;
+ }
+
+ prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
+ (send_deauth ? IEEE80211_STYPE_DEAUTH :
+ (reassoc ? IEEE80211_STYPE_REASSOC_RESP :
+ IEEE80211_STYPE_ASSOC_RESP)),
+ body, (u8 *) pos - (u8 *) body,
+ hdr->addr2,
+ send_deauth ? 0 : local->ap->tx_callback_assoc);
+
+ if (sta) {
+ if (resp == WLAN_STATUS_SUCCESS) {
+ sta->last_rx = jiffies;
+ /* STA will be marked associated from TX callback, if
+ * AssocResp is ACKed */
+ }
+ atomic_dec(&sta->users);
+ }
+
+#if 0
+ PDEBUG(DEBUG_AP, "%s: " MACSTR " %sassoc (len=%d prev_ap=" MACSTR
+ ") => %d(%d) (%s)\n",
+ dev->name, MAC2STR(hdr->addr2), reassoc ? "re" : "", len,
+ MAC2STR(prev_ap), resp, send_deauth, txt);
+#endif
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_deauth(local_info_t *local, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ struct net_device *dev = local->dev;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ char *body = (char *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+ int len;
+ u16 reason_code, *pos;
+ struct sta_info *sta = NULL;
+
+ len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+ if (len < 2) {
+ printk("handle_deauth - too short payload (len=%d)\n", len);
+ return;
+ }
+
+ pos = (u16 *) body;
+ reason_code = __le16_to_cpu(*pos);
+
+ PDEBUG(DEBUG_AP, "%s: deauthentication: " MACSTR " len=%d, "
+ "reason_code=%d\n", dev->name, MAC2STR(hdr->addr2), len,
+ reason_code);
+
+ spin_lock_bh(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr2);
+ if (sta != NULL) {
+ if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+ hostap_event_expired_sta(local->dev, sta);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ }
+ spin_unlock_bh(&local->ap->sta_table_lock);
+ if (sta == NULL) {
+ printk("%s: deauthentication from " MACSTR ", "
+ "reason_code=%d, but STA not authenticated\n", dev->name,
+ MAC2STR(hdr->addr2), reason_code);
+ }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_disassoc(local_info_t *local, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ struct net_device *dev = local->dev;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
+ int len;
+ u16 reason_code, *pos;
+ struct sta_info *sta = NULL;
+
+ len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+ if (len < 2) {
+ printk("handle_disassoc - too short payload (len=%d)\n", len);
+ return;
+ }
+
+ pos = (u16 *) body;
+ reason_code = __le16_to_cpu(*pos);
+
+ PDEBUG(DEBUG_AP, "%s: disassociation: " MACSTR " len=%d, "
+ "reason_code=%d\n", dev->name, MAC2STR(hdr->addr2), len,
+ reason_code);
+
+ spin_lock_bh(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr2);
+ if (sta != NULL) {
+ if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+ hostap_event_expired_sta(local->dev, sta);
+ sta->flags &= ~WLAN_STA_ASSOC;
+ }
+ spin_unlock_bh(&local->ap->sta_table_lock);
+ if (sta == NULL) {
+ printk("%s: disassociation from " MACSTR ", "
+ "reason_code=%d, but STA not authenticated\n",
+ dev->name, MAC2STR(hdr->addr2), reason_code);
+ }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void ap_handle_data_nullfunc(local_info_t *local,
+ struct ieee80211_hdr *hdr)
+{
+ struct net_device *dev = local->dev;
+
+ /* some STA f/w's seem to require control::ACK frame for
+ * data::nullfunc, but at least Prism2 station f/w version 0.8.0 does
+ * not send this..
+ * send control::ACK for the data::nullfunc */
+
+ printk(KERN_DEBUG "Sending control::ACK for data::nullfunc\n");
+ prism2_send_mgmt(dev, IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK,
+ NULL, 0, hdr->addr2, 0);
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void ap_handle_dropped_data(local_info_t *local,
+ struct ieee80211_hdr *hdr)
+{
+ struct net_device *dev = local->dev;
+ struct sta_info *sta;
+ u16 reason;
+
+ spin_lock_bh(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr2);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock_bh(&local->ap->sta_table_lock);
+
+ if (sta != NULL && (sta->flags & WLAN_STA_ASSOC)) {
+ PDEBUG(DEBUG_AP, "ap_handle_dropped_data: STA is now okay?\n");
+ atomic_dec(&sta->users);
+ return;
+ }
+
+ reason = __constant_cpu_to_le16(
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+ prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
+ ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ?
+ IEEE80211_STYPE_DEAUTH : IEEE80211_STYPE_DISASSOC),
+ (char *) &reason, sizeof(reason), hdr->addr2, 0);
+
+ if (sta)
+ atomic_dec(&sta->users);
+}
+
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void pspoll_send_buffered(local_info_t *local, struct sta_info *sta,
+ struct sk_buff *skb)
+{
+ struct hostap_skb_tx_data *meta;
+
+ if (!(sta->flags & WLAN_STA_PS)) {
+ /* Station has moved to non-PS mode, so send all buffered
+ * frames using normal device queue. */
+ dev_queue_xmit(skb);
+ return;
+ }
+
+ /* add a flag for hostap_handle_sta_tx() to know that this skb should
+ * be passed through even though STA is using PS */
+ meta = (struct hostap_skb_tx_data *) skb->cb;
+ meta->flags |= HOSTAP_TX_FLAGS_BUFFERED_FRAME;
+ if (!skb_queue_empty(&sta->tx_buf)) {
+ /* indicate to STA that more frames follow */
+ meta->flags |= HOSTAP_TX_FLAGS_ADD_MOREDATA;
+ }
+ dev_queue_xmit(skb);
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_pspoll(local_info_t *local,
+ struct ieee80211_hdr *hdr,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ struct net_device *dev = local->dev;
+ struct sta_info *sta;
+ u16 aid;
+ struct sk_buff *skb;
+
+ PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=" MACSTR ", TA=" MACSTR
+ " PWRMGT=%d\n",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ !!(le16_to_cpu(hdr->frame_ctl) & IEEE80211_FCTL_PM));
+
+ if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
+ PDEBUG(DEBUG_AP, "handle_pspoll - addr1(BSSID)=" MACSTR
+ " not own MAC\n", MAC2STR(hdr->addr1));
+ return;
+ }
+
+ aid = __le16_to_cpu(hdr->duration_id);
+ if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) {
+ PDEBUG(DEBUG_PS, " PSPOLL and AID[15:14] not set\n");
+ return;
+ }
+ aid &= ~BIT(15) & ~BIT(14);
+ if (aid == 0 || aid > MAX_AID_TABLE_SIZE) {
+ PDEBUG(DEBUG_PS, " invalid aid=%d\n", aid);
+ return;
+ }
+ PDEBUG(DEBUG_PS2, " aid=%d\n", aid);
+
+ spin_lock_bh(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr2);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock_bh(&local->ap->sta_table_lock);
+
+ if (sta == NULL) {
+ PDEBUG(DEBUG_PS, " STA not found\n");
+ return;
+ }
+ if (sta->aid != aid) {
+ PDEBUG(DEBUG_PS, " received aid=%i does not match with "
+ "assoc.aid=%d\n", aid, sta->aid);
+ return;
+ }
+
+ /* FIX: todo:
+ * - add timeout for buffering (clear aid in TIM vector if buffer timed
+ * out (expiry time must be longer than ListenInterval for
+ * the corresponding STA; "8802-11: 11.2.1.9 AP aging function"
+ * - what to do, if buffered, pspolled, and sent frame is not ACKed by
+ * sta; store buffer for later use and leave TIM aid bit set? use
+ * TX event to check whether frame was ACKed?
+ */
+
+ while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) {
+ /* send buffered frame .. */
+ PDEBUG(DEBUG_PS2, "Sending buffered frame to STA after PS POLL"
+ " (buffer_count=%d)\n", skb_queue_len(&sta->tx_buf));
+
+ pspoll_send_buffered(local, sta, skb);
+
+ if (sta->flags & WLAN_STA_PS) {
+ /* send only one buffered packet per PS Poll */
+ /* FIX: should ignore further PS Polls until the
+ * buffered packet that was just sent is acknowledged
+ * (Tx or TxExc event) */
+ break;
+ }
+ }
+
+ if (skb_queue_empty(&sta->tx_buf)) {
+ /* try to clear aid from TIM */
+ if (!(sta->flags & WLAN_STA_TIM))
+ PDEBUG(DEBUG_PS2, "Re-unsetting TIM for aid %d\n",
+ aid);
+ hostap_set_tim(local, aid, 0);
+ sta->flags &= ~WLAN_STA_TIM;
+ }
+
+ atomic_dec(&sta->users);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+static void handle_wds_oper_queue(void *data)
+{
+ local_info_t *local = data;
+ struct wds_oper_data *entry, *prev;
+
+ spin_lock_bh(&local->lock);
+ entry = local->ap->wds_oper_entries;
+ local->ap->wds_oper_entries = NULL;
+ spin_unlock_bh(&local->lock);
+
+ while (entry) {
+ PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection "
+ "to AP " MACSTR "\n",
+ local->dev->name,
+ entry->type == WDS_ADD ? "adding" : "removing",
+ MAC2STR(entry->addr));
+ if (entry->type == WDS_ADD)
+ prism2_wds_add(local, entry->addr, 0);
+ else if (entry->type == WDS_DEL)
+ prism2_wds_del(local, entry->addr, 0, 1);
+
+ prev = entry;
+ entry = entry->next;
+ kfree(prev);
+ }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_beacon(local_info_t *local, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
+ int len, left;
+ u16 *pos, beacon_int, capability;
+ char *ssid = NULL;
+ unsigned char *supp_rates = NULL;
+ int ssid_len = 0, supp_rates_len = 0;
+ struct sta_info *sta = NULL;
+ int new_sta = 0, channel = -1;
+
+ len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+ if (len < 8 + 2 + 2) {
+ printk(KERN_DEBUG "handle_beacon - too short payload "
+ "(len=%d)\n", len);
+ return;
+ }
+
+ pos = (u16 *) body;
+ left = len;
+
+ /* Timestamp (8 octets) */
+ pos += 4; left -= 8;
+ /* Beacon interval (2 octets) */
+ beacon_int = __le16_to_cpu(*pos);
+ pos++; left -= 2;
+ /* Capability information (2 octets) */
+ capability = __le16_to_cpu(*pos);
+ pos++; left -= 2;
+
+ if (local->ap->ap_policy != AP_OTHER_AP_EVEN_IBSS &&
+ capability & WLAN_CAPABILITY_IBSS)
+ return;
+
+ if (left >= 2) {
+ unsigned int ileft;
+ unsigned char *u = (unsigned char *) pos;
+
+ if (*u == WLAN_EID_SSID) {
+ u++; left--;
+ ileft = *u;
+ u++; left--;
+
+ if (ileft > left || ileft > MAX_SSID_LEN) {
+ PDEBUG(DEBUG_AP, "SSID: overflow\n");
+ return;
+ }
+
+ if (local->ap->ap_policy == AP_OTHER_AP_SAME_SSID &&
+ (ileft != strlen(local->essid) ||
+ memcmp(local->essid, u, ileft) != 0)) {
+ /* not our SSID */
+ return;
+ }
+
+ ssid = u;
+ ssid_len = ileft;
+
+ u += ileft;
+ left -= ileft;
+ }
+
+ if (*u == WLAN_EID_SUPP_RATES) {
+ u++; left--;
+ ileft = *u;
+ u++; left--;
+
+ if (ileft > left || ileft == 0 || ileft > 8) {
+ PDEBUG(DEBUG_AP, " - SUPP_RATES len error\n");
+ return;
+ }
+
+ supp_rates = u;
+ supp_rates_len = ileft;
+
+ u += ileft;
+ left -= ileft;
+ }
+
+ if (*u == WLAN_EID_DS_PARAMS) {
+ u++; left--;
+ ileft = *u;
+ u++; left--;
+
+ if (ileft > left || ileft != 1) {
+ PDEBUG(DEBUG_AP, " - DS_PARAMS len error\n");
+ return;
+ }
+
+ channel = *u;
+
+ u += ileft;
+ left -= ileft;
+ }
+ }
+
+ spin_lock_bh(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr2);
+ if (sta != NULL)
+ atomic_inc(&sta->users);
+ spin_unlock_bh(&local->ap->sta_table_lock);
+
+ if (sta == NULL) {
+ /* add new AP */
+ new_sta = 1;
+ sta = ap_add_sta(local->ap, hdr->addr2);
+ if (sta == NULL) {
+ printk(KERN_INFO "prism2: kmalloc failed for AP "
+ "data structure\n");
+ return;
+ }
+ hostap_event_new_sta(local->dev, sta);
+
+ /* mark APs authentication and associated for pseudo ad-hoc
+ * style communication */
+ sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
+
+ if (local->ap->autom_ap_wds) {
+ hostap_wds_link_oper(local, sta->addr, WDS_ADD);
+ }
+ }
+
+ sta->ap = 1;
+ if (ssid) {
+ sta->u.ap.ssid_len = ssid_len;
+ memcpy(sta->u.ap.ssid, ssid, ssid_len);
+ sta->u.ap.ssid[ssid_len] = '\0';
+ } else {
+ sta->u.ap.ssid_len = 0;
+ sta->u.ap.ssid[0] = '\0';
+ }
+ sta->u.ap.channel = channel;
+ sta->rx_packets++;
+ sta->rx_bytes += len;
+ sta->u.ap.last_beacon = sta->last_rx = jiffies;
+ sta->capability = capability;
+ sta->listen_interval = beacon_int;
+
+ atomic_dec(&sta->users);
+
+ if (new_sta) {
+ memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
+ memcpy(sta->supported_rates, supp_rates, supp_rates_len);
+ prism2_check_tx_rates(sta);
+ }
+}
+
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+/* Called only as a tasklet. */
+static void handle_ap_item(local_info_t *local, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats)
+{
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ struct net_device *dev = local->dev;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+ u16 fc, type, stype;
+ struct ieee80211_hdr *hdr;
+
+ /* FIX: should give skb->len to handler functions and check that the
+ * buffer is long enough */
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ if (!local->hostapd && type == IEEE80211_FTYPE_DATA) {
+ PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n");
+
+ if (!(fc & IEEE80211_FCTL_TODS) ||
+ (fc & IEEE80211_FCTL_FROMDS)) {
+ if (stype == IEEE80211_STYPE_NULLFUNC) {
+ /* no ToDS nullfunc seems to be used to check
+ * AP association; so send reject message to
+ * speed up re-association */
+ ap_handle_dropped_data(local, hdr);
+ goto done;
+ }
+ PDEBUG(DEBUG_AP, " not ToDS frame (fc=0x%04x)\n",
+ fc);
+ goto done;
+ }
+
+ if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
+ PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)="
+ MACSTR " not own MAC\n",
+ MAC2STR(hdr->addr1));
+ goto done;
+ }
+
+ if (local->ap->nullfunc_ack &&
+ stype == IEEE80211_STYPE_NULLFUNC)
+ ap_handle_data_nullfunc(local, hdr);
+ else
+ ap_handle_dropped_data(local, hdr);
+ goto done;
+ }
+
+ if (type == IEEE80211_FTYPE_MGMT && stype == IEEE80211_STYPE_BEACON) {
+ handle_beacon(local, skb, rx_stats);
+ goto done;
+ }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ if (type == IEEE80211_FTYPE_CTL && stype == IEEE80211_STYPE_PSPOLL) {
+ handle_pspoll(local, hdr, rx_stats);
+ goto done;
+ }
+
+ if (local->hostapd) {
+ PDEBUG(DEBUG_AP, "Unknown frame in AP queue: type=0x%02x "
+ "subtype=0x%02x\n", type, stype);
+ goto done;
+ }
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ if (type != IEEE80211_FTYPE_MGMT) {
+ PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n");
+ goto done;
+ }
+
+ if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
+ PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=" MACSTR
+ " not own MAC\n", MAC2STR(hdr->addr1));
+ goto done;
+ }
+
+ if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN)) {
+ PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=" MACSTR
+ " not own MAC\n", MAC2STR(hdr->addr3));
+ goto done;
+ }
+
+ switch (stype) {
+ case IEEE80211_STYPE_ASSOC_REQ:
+ handle_assoc(local, skb, rx_stats, 0);
+ break;
+ case IEEE80211_STYPE_ASSOC_RESP:
+ PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n");
+ break;
+ case IEEE80211_STYPE_REASSOC_REQ:
+ handle_assoc(local, skb, rx_stats, 1);
+ break;
+ case IEEE80211_STYPE_REASSOC_RESP:
+ PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n");
+ break;
+ case IEEE80211_STYPE_ATIM:
+ PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n");
+ break;
+ case IEEE80211_STYPE_DISASSOC:
+ handle_disassoc(local, skb, rx_stats);
+ break;
+ case IEEE80211_STYPE_AUTH:
+ handle_authen(local, skb, rx_stats);
+ break;
+ case IEEE80211_STYPE_DEAUTH:
+ handle_deauth(local, skb, rx_stats);
+ break;
+ default:
+ PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n",
+ stype >> 4);
+ break;
+ }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ done:
+ dev_kfree_skb(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+void hostap_rx(struct net_device *dev, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 fc;
+ struct ieee80211_hdr *hdr;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (skb->len < 16)
+ goto drop;
+
+ local->stats.rx_packets++;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL &&
+ WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_BEACON)
+ goto drop;
+
+ skb->protocol = __constant_htons(ETH_P_HOSTAP);
+ handle_ap_item(local, skb, rx_stats);
+ return;
+
+ drop:
+ dev_kfree_skb(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void schedule_packet_send(local_info_t *local, struct sta_info *sta)
+{
+ struct sk_buff *skb;
+ struct ieee80211_hdr *hdr;
+ struct hostap_80211_rx_status rx_stats;
+
+ if (skb_queue_empty(&sta->tx_buf))
+ return;
+
+ skb = dev_alloc_skb(16);
+ if (skb == NULL) {
+ printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc "
+ "failed\n", local->dev->name);
+ return;
+ }
+
+ hdr = (struct ieee80211_hdr *) skb_put(skb, 16);
+
+ /* Generate a fake pspoll frame to start packet delivery */
+ hdr->frame_ctl = __constant_cpu_to_le16(
+ IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
+ memcpy(hdr->addr1, local->dev->dev_addr, ETH_ALEN);
+ memcpy(hdr->addr2, sta->addr, ETH_ALEN);
+ hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14));
+
+ PDEBUG(DEBUG_PS2, "%s: Scheduling buffered packet delivery for "
+ "STA " MACSTR "\n", local->dev->name, MAC2STR(sta->addr));
+
+ skb->dev = local->dev;
+
+ memset(&rx_stats, 0, sizeof(rx_stats));
+ hostap_rx(local->dev, skb, &rx_stats);
+}
+
+
+static int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
+ struct iw_quality qual[], int buf_size,
+ int aplist)
+{
+ struct ap_data *ap = local->ap;
+ struct list_head *ptr;
+ int count = 0;
+
+ spin_lock_bh(&ap->sta_table_lock);
+
+ for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
+ ptr = ptr->next) {
+ struct sta_info *sta = (struct sta_info *) ptr;
+
+ if (aplist && !sta->ap)
+ continue;
+ addr[count].sa_family = ARPHRD_ETHER;
+ memcpy(addr[count].sa_data, sta->addr, ETH_ALEN);
+ if (sta->last_rx_silence == 0)
+ qual[count].qual = sta->last_rx_signal < 27 ?
+ 0 : (sta->last_rx_signal - 27) * 92 / 127;
+ else
+ qual[count].qual = sta->last_rx_signal -
+ sta->last_rx_silence - 35;
+ qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
+ qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
+ qual[count].updated = sta->last_rx_updated;
+
+ sta->last_rx_updated = 0;
+
+ count++;
+ if (count >= buf_size)
+ break;
+ }
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ return count;
+}
+
+
+/* Translate our list of Access Points & Stations to a card independant
+ * format that the Wireless Tools will understand - Jean II */
+static int prism2_ap_translate_scan(struct net_device *dev, char *buffer)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct ap_data *ap;
+ struct list_head *ptr;
+ struct iw_event iwe;
+ char *current_ev = buffer;
+ char *end_buf = buffer + IW_SCAN_MAX_DATA;
+#if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT)
+ char buf[64];
+#endif
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ ap = local->ap;
+
+ spin_lock_bh(&ap->sta_table_lock);
+
+ for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
+ ptr = ptr->next) {
+ struct sta_info *sta = (struct sta_info *) ptr;
+
+ /* First entry *MUST* be the AP MAC address */
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN);
+ iwe.len = IW_EV_ADDR_LEN;
+ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+ IW_EV_ADDR_LEN);
+
+ /* Use the mode to indicate if it's a station or
+ * an Access Point */
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWMODE;
+ if (sta->ap)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_INFRA;
+ iwe.len = IW_EV_UINT_LEN;
+ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+ IW_EV_UINT_LEN);
+
+ /* Some quality */
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVQUAL;
+ if (sta->last_rx_silence == 0)
+ iwe.u.qual.qual = sta->last_rx_signal < 27 ?
+ 0 : (sta->last_rx_signal - 27) * 92 / 127;
+ else
+ iwe.u.qual.qual = sta->last_rx_signal -
+ sta->last_rx_silence - 35;
+ iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
+ iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
+ iwe.u.qual.updated = sta->last_rx_updated;
+ iwe.len = IW_EV_QUAL_LEN;
+ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+ IW_EV_QUAL_LEN);
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ if (sta->ap) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.length = sta->u.ap.ssid_len;
+ iwe.u.data.flags = 1;
+ current_ev = iwe_stream_add_point(current_ev, end_buf,
+ &iwe,
+ sta->u.ap.ssid);
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWENCODE;
+ if (sta->capability & WLAN_CAPABILITY_PRIVACY)
+ iwe.u.data.flags =
+ IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ current_ev = iwe_stream_add_point(current_ev, end_buf,
+ &iwe,
+ sta->u.ap.ssid
+ /* 0 byte memcpy */);
+
+ if (sta->u.ap.channel > 0 &&
+ sta->u.ap.channel <= FREQ_COUNT) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = freq_list[sta->u.ap.channel - 1]
+ * 100000;
+ iwe.u.freq.e = 1;
+ current_ev = iwe_stream_add_event(
+ current_ev, end_buf, &iwe,
+ IW_EV_FREQ_LEN);
+ }
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, "beacon_interval=%d",
+ sta->listen_interval);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(current_ev, end_buf,
+ &iwe, buf);
+ }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ sta->last_rx_updated = 0;
+
+ /* To be continued, we should make good use of IWEVCUSTOM */
+ }
+
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ return current_ev - buffer;
+}
+
+
+static int prism2_hostapd_add_sta(struct ap_data *ap,
+ struct prism2_hostapd_param *param)
+{
+ struct sta_info *sta;
+
+ spin_lock_bh(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, param->sta_addr);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ if (sta == NULL) {
+ sta = ap_add_sta(ap, param->sta_addr);
+ if (sta == NULL)
+ return -1;
+ }
+
+ if (!(sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+ hostap_event_new_sta(sta->local->dev, sta);
+
+ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+ sta->last_rx = jiffies;
+ sta->aid = param->u.add_sta.aid;
+ sta->capability = param->u.add_sta.capability;
+ sta->tx_supp_rates = param->u.add_sta.tx_supp_rates;
+ if (sta->tx_supp_rates & WLAN_RATE_1M)
+ sta->supported_rates[0] = 2;
+ if (sta->tx_supp_rates & WLAN_RATE_2M)
+ sta->supported_rates[1] = 4;
+ if (sta->tx_supp_rates & WLAN_RATE_5M5)
+ sta->supported_rates[2] = 11;
+ if (sta->tx_supp_rates & WLAN_RATE_11M)
+ sta->supported_rates[3] = 22;
+ prism2_check_tx_rates(sta);
+ atomic_dec(&sta->users);
+ return 0;
+}
+
+
+static int prism2_hostapd_remove_sta(struct ap_data *ap,
+ struct prism2_hostapd_param *param)
+{
+ struct sta_info *sta;
+
+ spin_lock_bh(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, param->sta_addr);
+ if (sta) {
+ ap_sta_hash_del(ap, sta);
+ list_del(&sta->list);
+ }
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ if (!sta)
+ return -ENOENT;
+
+ if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+ hostap_event_expired_sta(sta->local->dev, sta);
+ ap_free_sta(ap, sta);
+
+ return 0;
+}
+
+
+static int prism2_hostapd_get_info_sta(struct ap_data *ap,
+ struct prism2_hostapd_param *param)
+{
+ struct sta_info *sta;
+
+ spin_lock_bh(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, param->sta_addr);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ if (!sta)
+ return -ENOENT;
+
+ param->u.get_info_sta.inactive_sec = (jiffies - sta->last_rx) / HZ;
+
+ atomic_dec(&sta->users);
+
+ return 1;
+}
+
+
+static int prism2_hostapd_set_flags_sta(struct ap_data *ap,
+ struct prism2_hostapd_param *param)
+{
+ struct sta_info *sta;
+
+ spin_lock_bh(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, param->sta_addr);
+ if (sta) {
+ sta->flags |= param->u.set_flags_sta.flags_or;
+ sta->flags &= param->u.set_flags_sta.flags_and;
+ }
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ if (!sta)
+ return -ENOENT;
+
+ return 0;
+}
+
+
+static int prism2_hostapd_sta_clear_stats(struct ap_data *ap,
+ struct prism2_hostapd_param *param)
+{
+ struct sta_info *sta;
+ int rate;
+
+ spin_lock_bh(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, param->sta_addr);
+ if (sta) {
+ sta->rx_packets = sta->tx_packets = 0;
+ sta->rx_bytes = sta->tx_bytes = 0;
+ for (rate = 0; rate < WLAN_RATE_COUNT; rate++) {
+ sta->tx_count[rate] = 0;
+ sta->rx_count[rate] = 0;
+ }
+ }
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ if (!sta)
+ return -ENOENT;
+
+ return 0;
+}
+
+
+static int prism2_hostapd(struct ap_data *ap,
+ struct prism2_hostapd_param *param)
+{
+ switch (param->cmd) {
+ case PRISM2_HOSTAPD_FLUSH:
+ ap_control_kickall(ap);
+ return 0;
+ case PRISM2_HOSTAPD_ADD_STA:
+ return prism2_hostapd_add_sta(ap, param);
+ case PRISM2_HOSTAPD_REMOVE_STA:
+ return prism2_hostapd_remove_sta(ap, param);
+ case PRISM2_HOSTAPD_GET_INFO_STA:
+ return prism2_hostapd_get_info_sta(ap, param);
+ case PRISM2_HOSTAPD_SET_FLAGS_STA:
+ return prism2_hostapd_set_flags_sta(ap, param);
+ case PRISM2_HOSTAPD_STA_CLEAR_STATS:
+ return prism2_hostapd_sta_clear_stats(ap, param);
+ default:
+ printk(KERN_WARNING "prism2_hostapd: unknown cmd=%d\n",
+ param->cmd);
+ return -EOPNOTSUPP;
+ }
+}
+
+
+/* Update station info for host-based TX rate control and return current
+ * TX rate */
+static int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev)
+{
+ int ret = sta->tx_rate;
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ sta->tx_count[sta->tx_rate_idx]++;
+ sta->tx_since_last_failure++;
+ sta->tx_consecutive_exc = 0;
+ if (sta->tx_since_last_failure >= WLAN_RATE_UPDATE_COUNT &&
+ sta->tx_rate_idx < sta->tx_max_rate) {
+ /* use next higher rate */
+ int old_rate, new_rate;
+ old_rate = new_rate = sta->tx_rate_idx;
+ while (new_rate < sta->tx_max_rate) {
+ new_rate++;
+ if (ap_tx_rate_ok(new_rate, sta, local)) {
+ sta->tx_rate_idx = new_rate;
+ break;
+ }
+ }
+ if (old_rate != sta->tx_rate_idx) {
+ switch (sta->tx_rate_idx) {
+ case 0: sta->tx_rate = 10; break;
+ case 1: sta->tx_rate = 20; break;
+ case 2: sta->tx_rate = 55; break;
+ case 3: sta->tx_rate = 110; break;
+ default: sta->tx_rate = 0; break;
+ }
+ PDEBUG(DEBUG_AP, "%s: STA " MACSTR " TX rate raised to"
+ " %d\n", dev->name, MAC2STR(sta->addr),
+ sta->tx_rate);
+ }
+ sta->tx_since_last_failure = 0;
+ }
+
+ return ret;
+}
+
+
+/* Called only from software IRQ. Called for each TX frame prior possible
+ * encryption and transmit. */
+ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx)
+{
+ struct sta_info *sta = NULL;
+ struct sk_buff *skb = tx->skb;
+ int set_tim, ret;
+ struct ieee80211_hdr *hdr;
+ struct hostap_skb_tx_data *meta;
+
+ meta = (struct hostap_skb_tx_data *) skb->cb;
+ ret = AP_TX_CONTINUE;
+ if (local->ap == NULL || skb->len < 10 ||
+ meta->iface->type == HOSTAP_INTERFACE_STA)
+ goto out;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+
+ if (hdr->addr1[0] & 0x01) {
+ /* broadcast/multicast frame - no AP related processing */
+ goto out;
+ }
+
+ /* unicast packet - check whether destination STA is associated */
+ spin_lock(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr1);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock(&local->ap->sta_table_lock);
+
+ if (local->iw_mode == IW_MODE_MASTER && sta == NULL &&
+ !(meta->flags & HOSTAP_TX_FLAGS_WDS) &&
+ meta->iface->type != HOSTAP_INTERFACE_MASTER &&
+ meta->iface->type != HOSTAP_INTERFACE_AP) {
+#if 0
+ /* This can happen, e.g., when wlan0 is added to a bridge and
+ * bridging code does not know which port is the correct target
+ * for a unicast frame. In this case, the packet is send to all
+ * ports of the bridge. Since this is a valid scenario, do not
+ * print out any errors here. */
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "AP: drop packet to non-associated "
+ "STA " MACSTR "\n", MAC2STR(hdr->addr1));
+ }
+#endif
+ local->ap->tx_drop_nonassoc++;
+ ret = AP_TX_DROP;
+ goto out;
+ }
+
+ if (sta == NULL)
+ goto out;
+
+ if (!(sta->flags & WLAN_STA_AUTHORIZED))
+ ret = AP_TX_CONTINUE_NOT_AUTHORIZED;
+
+ /* Set tx_rate if using host-based TX rate control */
+ if (!local->fw_tx_rate_control)
+ local->ap->last_tx_rate = meta->rate =
+ ap_update_sta_tx_rate(sta, local->dev);
+
+ if (local->iw_mode != IW_MODE_MASTER)
+ goto out;
+
+ if (!(sta->flags & WLAN_STA_PS))
+ goto out;
+
+ if (meta->flags & HOSTAP_TX_FLAGS_ADD_MOREDATA) {
+ /* indicate to STA that more frames follow */
+ hdr->frame_ctl |=
+ __constant_cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ }
+
+ if (meta->flags & HOSTAP_TX_FLAGS_BUFFERED_FRAME) {
+ /* packet was already buffered and now send due to
+ * PS poll, so do not rebuffer it */
+ goto out;
+ }
+
+ if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) {
+ PDEBUG(DEBUG_PS, "%s: No more space in STA (" MACSTR ")'s PS "
+ "mode buffer\n", local->dev->name, MAC2STR(sta->addr));
+ /* Make sure that TIM is set for the station (it might not be
+ * after AP wlan hw reset). */
+ /* FIX: should fix hw reset to restore bits based on STA
+ * buffer state.. */
+ hostap_set_tim(local, sta->aid, 1);
+ sta->flags |= WLAN_STA_TIM;
+ ret = AP_TX_DROP;
+ goto out;
+ }
+
+ /* STA in PS mode, buffer frame for later delivery */
+ set_tim = skb_queue_empty(&sta->tx_buf);
+ skb_queue_tail(&sta->tx_buf, skb);
+ /* FIX: could save RX time to skb and expire buffered frames after
+ * some time if STA does not poll for them */
+
+ if (set_tim) {
+ if (sta->flags & WLAN_STA_TIM)
+ PDEBUG(DEBUG_PS2, "Re-setting TIM for aid %d\n",
+ sta->aid);
+ hostap_set_tim(local, sta->aid, 1);
+ sta->flags |= WLAN_STA_TIM;
+ }
+
+ ret = AP_TX_BUFFERED;
+
+ out:
+ if (sta != NULL) {
+ if (ret == AP_TX_CONTINUE ||
+ ret == AP_TX_CONTINUE_NOT_AUTHORIZED) {
+ sta->tx_packets++;
+ sta->tx_bytes += skb->len;
+ sta->last_tx = jiffies;
+ }
+
+ if ((ret == AP_TX_CONTINUE ||
+ ret == AP_TX_CONTINUE_NOT_AUTHORIZED) &&
+ sta->crypt && tx->host_encrypt) {
+ tx->crypt = sta->crypt;
+ tx->sta_ptr = sta; /* hostap_handle_sta_release() will
+ * be called to release sta info
+ * later */
+ } else
+ atomic_dec(&sta->users);
+ }
+
+ return ret;
+}
+
+
+void hostap_handle_sta_release(void *ptr)
+{
+ struct sta_info *sta = ptr;
+ atomic_dec(&sta->users);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb)
+{
+ struct sta_info *sta;
+ struct ieee80211_hdr *hdr;
+ struct hostap_skb_tx_data *meta;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ meta = (struct hostap_skb_tx_data *) skb->cb;
+
+ spin_lock(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr1);
+ if (!sta) {
+ spin_unlock(&local->ap->sta_table_lock);
+ PDEBUG(DEBUG_AP, "%s: Could not find STA " MACSTR " for this "
+ "TX error (@%lu)\n",
+ local->dev->name, MAC2STR(hdr->addr1), jiffies);
+ return;
+ }
+
+ sta->tx_since_last_failure = 0;
+ sta->tx_consecutive_exc++;
+
+ if (sta->tx_consecutive_exc >= WLAN_RATE_DECREASE_THRESHOLD &&
+ sta->tx_rate_idx > 0 && meta->rate <= sta->tx_rate) {
+ /* use next lower rate */
+ int old, rate;
+ old = rate = sta->tx_rate_idx;
+ while (rate > 0) {
+ rate--;
+ if (ap_tx_rate_ok(rate, sta, local)) {
+ sta->tx_rate_idx = rate;
+ break;
+ }
+ }
+ if (old != sta->tx_rate_idx) {
+ switch (sta->tx_rate_idx) {
+ case 0: sta->tx_rate = 10; break;
+ case 1: sta->tx_rate = 20; break;
+ case 2: sta->tx_rate = 55; break;
+ case 3: sta->tx_rate = 110; break;
+ default: sta->tx_rate = 0; break;
+ }
+ PDEBUG(DEBUG_AP, "%s: STA " MACSTR " TX rate lowered "
+ "to %d\n", local->dev->name, MAC2STR(sta->addr),
+ sta->tx_rate);
+ }
+ sta->tx_consecutive_exc = 0;
+ }
+ spin_unlock(&local->ap->sta_table_lock);
+}
+
+
+static void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta,
+ int pwrmgt, int type, int stype)
+{
+ if (pwrmgt && !(sta->flags & WLAN_STA_PS)) {
+ sta->flags |= WLAN_STA_PS;
+ PDEBUG(DEBUG_PS2, "STA " MACSTR " changed to use PS "
+ "mode (type=0x%02X, stype=0x%02X)\n",
+ MAC2STR(sta->addr), type >> 2, stype >> 4);
+ } else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) {
+ sta->flags &= ~WLAN_STA_PS;
+ PDEBUG(DEBUG_PS2, "STA " MACSTR " changed to not use "
+ "PS mode (type=0x%02X, stype=0x%02X)\n",
+ MAC2STR(sta->addr), type >> 2, stype >> 4);
+ if (type != IEEE80211_FTYPE_CTL ||
+ stype != IEEE80211_STYPE_PSPOLL)
+ schedule_packet_send(local, sta);
+ }
+}
+
+
+/* Called only as a tasklet (software IRQ). Called for each RX frame to update
+ * STA power saving state. pwrmgt is a flag from 802.11 frame_ctl field. */
+int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr)
+{
+ struct sta_info *sta;
+ u16 fc;
+
+ spin_lock(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr2);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock(&local->ap->sta_table_lock);
+
+ if (!sta)
+ return -1;
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM,
+ WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc));
+
+ atomic_dec(&sta->users);
+ return 0;
+}
+
+
+/* Called only as a tasklet (software IRQ). Called for each RX frame after
+ * getting RX header and payload from hardware. */
+ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
+ struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats,
+ int wds)
+{
+ int ret;
+ struct sta_info *sta;
+ u16 fc, type, stype;
+ struct ieee80211_hdr *hdr;
+
+ if (local->ap == NULL)
+ return AP_RX_CONTINUE;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ spin_lock(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr2);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock(&local->ap->sta_table_lock);
+
+ if (sta && !(sta->flags & WLAN_STA_AUTHORIZED))
+ ret = AP_RX_CONTINUE_NOT_AUTHORIZED;
+ else
+ ret = AP_RX_CONTINUE;
+
+
+ if (fc & IEEE80211_FCTL_TODS) {
+ if (!wds && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
+ if (local->hostapd) {
+ prism2_rx_80211(local->apdev, skb, rx_stats,
+ PRISM2_RX_NON_ASSOC);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ } else {
+ printk(KERN_DEBUG "%s: dropped received packet"
+ " from non-associated STA " MACSTR
+ " (type=0x%02x, subtype=0x%02x)\n",
+ dev->name, MAC2STR(hdr->addr2),
+ type >> 2, stype >> 4);
+ hostap_rx(dev, skb, rx_stats);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+ }
+ ret = AP_RX_EXIT;
+ goto out;
+ }
+ } else if (fc & IEEE80211_FCTL_FROMDS) {
+ if (!wds) {
+ /* FromDS frame - not for us; probably
+ * broadcast/multicast in another BSS - drop */
+ if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+ printk(KERN_DEBUG "Odd.. FromDS packet "
+ "received with own BSSID\n");
+ hostap_dump_rx_80211(dev->name, skb, rx_stats);
+ }
+ ret = AP_RX_DROP;
+ goto out;
+ }
+ } else if (stype == IEEE80211_STYPE_NULLFUNC && sta == NULL &&
+ memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+
+ if (local->hostapd) {
+ prism2_rx_80211(local->apdev, skb, rx_stats,
+ PRISM2_RX_NON_ASSOC);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ } else {
+ /* At least Lucent f/w seems to send data::nullfunc
+ * frames with no ToDS flag when the current AP returns
+ * after being unavailable for some time. Speed up
+ * re-association by informing the station about it not
+ * being associated. */
+ printk(KERN_DEBUG "%s: rejected received nullfunc "
+ "frame without ToDS from not associated STA "
+ MACSTR "\n",
+ dev->name, MAC2STR(hdr->addr2));
+ hostap_rx(dev, skb, rx_stats);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+ }
+ ret = AP_RX_EXIT;
+ goto out;
+ } else if (stype == IEEE80211_STYPE_NULLFUNC) {
+ /* At least Lucent cards seem to send periodic nullfunc
+ * frames with ToDS. Let these through to update SQ
+ * stats and PS state. Nullfunc frames do not contain
+ * any data and they will be dropped below. */
+ } else {
+ /* If BSSID (Addr3) is foreign, this frame is a normal
+ * broadcast frame from an IBSS network. Drop it silently.
+ * If BSSID is own, report the dropping of this frame. */
+ if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) {
+ printk(KERN_DEBUG "%s: dropped received packet from "
+ MACSTR " with no ToDS flag (type=0x%02x, "
+ "subtype=0x%02x)\n", dev->name,
+ MAC2STR(hdr->addr2), type >> 2, stype >> 4);
+ hostap_dump_rx_80211(dev->name, skb, rx_stats);
+ }
+ ret = AP_RX_DROP;
+ goto out;
+ }
+
+ if (sta) {
+ hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM,
+ type, stype);
+
+ sta->rx_packets++;
+ sta->rx_bytes += skb->len;
+ sta->last_rx = jiffies;
+ }
+
+ if (local->ap->nullfunc_ack && stype == IEEE80211_STYPE_NULLFUNC &&
+ fc & IEEE80211_FCTL_TODS) {
+ if (local->hostapd) {
+ prism2_rx_80211(local->apdev, skb, rx_stats,
+ PRISM2_RX_NULLFUNC_ACK);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ } else {
+ /* some STA f/w's seem to require control::ACK frame
+ * for data::nullfunc, but Prism2 f/w 0.8.0 (at least
+ * from Compaq) does not send this.. Try to generate
+ * ACK for these frames from the host driver to make
+ * power saving work with, e.g., Lucent WaveLAN f/w */
+ hostap_rx(dev, skb, rx_stats);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+ }
+ ret = AP_RX_EXIT;
+ goto out;
+ }
+
+ out:
+ if (sta)
+ atomic_dec(&sta->users);
+
+ return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_handle_sta_crypto(local_info_t *local,
+ struct ieee80211_hdr *hdr,
+ struct ieee80211_crypt_data **crypt,
+ void **sta_ptr)
+{
+ struct sta_info *sta;
+
+ spin_lock(&local->ap->sta_table_lock);
+ sta = ap_get_sta(local->ap, hdr->addr2);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock(&local->ap->sta_table_lock);
+
+ if (!sta)
+ return -1;
+
+ if (sta->crypt) {
+ *crypt = sta->crypt;
+ *sta_ptr = sta;
+ /* hostap_handle_sta_release() will be called to release STA
+ * info */
+ } else
+ atomic_dec(&sta->users);
+
+ return 0;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr)
+{
+ struct sta_info *sta;
+ int ret = 0;
+
+ spin_lock(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, sta_addr);
+ if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+ ret = 1;
+ spin_unlock(&ap->sta_table_lock);
+
+ return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr)
+{
+ struct sta_info *sta;
+ int ret = 0;
+
+ spin_lock(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, sta_addr);
+ if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap &&
+ ((sta->flags & WLAN_STA_AUTHORIZED) ||
+ ap->local->ieee_802_1x == 0))
+ ret = 1;
+ spin_unlock(&ap->sta_table_lock);
+
+ return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_add_sta(struct ap_data *ap, u8 *sta_addr)
+{
+ struct sta_info *sta;
+ int ret = 1;
+
+ if (!ap)
+ return -1;
+
+ spin_lock(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, sta_addr);
+ if (sta)
+ ret = 0;
+ spin_unlock(&ap->sta_table_lock);
+
+ if (ret == 1) {
+ sta = ap_add_sta(ap, sta_addr);
+ if (!sta)
+ ret = -1;
+ sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
+ sta->ap = 1;
+ memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
+ /* No way of knowing which rates are supported since we did not
+ * get supported rates element from beacon/assoc req. Assume
+ * that remote end supports all 802.11b rates. */
+ sta->supported_rates[0] = 0x82;
+ sta->supported_rates[1] = 0x84;
+ sta->supported_rates[2] = 0x0b;
+ sta->supported_rates[3] = 0x16;
+ sta->tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M |
+ WLAN_RATE_5M5 | WLAN_RATE_11M;
+ sta->tx_rate = 110;
+ sta->tx_max_rate = sta->tx_rate_idx = 3;
+ }
+
+ return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_update_rx_stats(struct ap_data *ap,
+ struct ieee80211_hdr *hdr,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ struct sta_info *sta;
+
+ if (!ap)
+ return -1;
+
+ spin_lock(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, hdr->addr2);
+ if (sta) {
+ sta->last_rx_silence = rx_stats->noise;
+ sta->last_rx_signal = rx_stats->signal;
+ sta->last_rx_rate = rx_stats->rate;
+ sta->last_rx_updated = 7;
+ if (rx_stats->rate == 10)
+ sta->rx_count[0]++;
+ else if (rx_stats->rate == 20)
+ sta->rx_count[1]++;
+ else if (rx_stats->rate == 55)
+ sta->rx_count[2]++;
+ else if (rx_stats->rate == 110)
+ sta->rx_count[3]++;
+ }
+ spin_unlock(&ap->sta_table_lock);
+
+ return sta ? 0 : -1;
+}
+
+
+void hostap_update_rates(local_info_t *local)
+{
+ struct list_head *ptr;
+ struct ap_data *ap = local->ap;
+
+ if (!ap)
+ return;
+
+ spin_lock_bh(&ap->sta_table_lock);
+ for (ptr = ap->sta_list.next; ptr != &ap->sta_list; ptr = ptr->next) {
+ struct sta_info *sta = (struct sta_info *) ptr;
+ prism2_check_tx_rates(sta);
+ }
+ spin_unlock_bh(&ap->sta_table_lock);
+}
+
+
+static void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
+ struct ieee80211_crypt_data ***crypt)
+{
+ struct sta_info *sta;
+
+ spin_lock_bh(&ap->sta_table_lock);
+ sta = ap_get_sta(ap, addr);
+ if (sta)
+ atomic_inc(&sta->users);
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ if (!sta && permanent)
+ sta = ap_add_sta(ap, addr);
+
+ if (!sta)
+ return NULL;
+
+ if (permanent)
+ sta->flags |= WLAN_STA_PERM;
+
+ *crypt = &sta->crypt;
+
+ return sta;
+}
+
+
+void hostap_add_wds_links(local_info_t *local)
+{
+ struct ap_data *ap = local->ap;
+ struct list_head *ptr;
+
+ spin_lock_bh(&ap->sta_table_lock);
+ list_for_each(ptr, &ap->sta_list) {
+ struct sta_info *sta = list_entry(ptr, struct sta_info, list);
+ if (sta->ap)
+ hostap_wds_link_oper(local, sta->addr, WDS_ADD);
+ }
+ spin_unlock_bh(&ap->sta_table_lock);
+
+ schedule_work(&local->ap->wds_oper_queue);
+}
+
+
+void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type)
+{
+ struct wds_oper_data *entry;
+
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return;
+ memcpy(entry->addr, addr, ETH_ALEN);
+ entry->type = type;
+ spin_lock_bh(&local->lock);
+ entry->next = local->ap->wds_oper_entries;
+ local->ap->wds_oper_entries = entry;
+ spin_unlock_bh(&local->lock);
+
+ schedule_work(&local->ap->wds_oper_queue);
+}
+
+
+EXPORT_SYMBOL(hostap_init_data);
+EXPORT_SYMBOL(hostap_init_ap_proc);
+EXPORT_SYMBOL(hostap_free_data);
+EXPORT_SYMBOL(hostap_check_sta_fw_version);
+EXPORT_SYMBOL(hostap_handle_sta_tx);
+EXPORT_SYMBOL(hostap_handle_sta_release);
+EXPORT_SYMBOL(hostap_handle_sta_tx_exc);
+EXPORT_SYMBOL(hostap_update_sta_ps);
+EXPORT_SYMBOL(hostap_handle_sta_rx);
+EXPORT_SYMBOL(hostap_is_sta_assoc);
+EXPORT_SYMBOL(hostap_is_sta_authorized);
+EXPORT_SYMBOL(hostap_add_sta);
+EXPORT_SYMBOL(hostap_update_rates);
+EXPORT_SYMBOL(hostap_add_wds_links);
+EXPORT_SYMBOL(hostap_wds_link_oper);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+EXPORT_SYMBOL(hostap_deauth_all_stas);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
diff --git a/drivers/net/wireless/hostap/hostap_ap.h b/drivers/net/wireless/hostap/hostap_ap.h
new file mode 100644
index 000000000000..816a52bcea8f
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_ap.h
@@ -0,0 +1,261 @@
+#ifndef HOSTAP_AP_H
+#define HOSTAP_AP_H
+
+/* AP data structures for STAs */
+
+/* maximum number of frames to buffer per STA */
+#define STA_MAX_TX_BUFFER 32
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_PS BIT(2)
+#define WLAN_STA_TIM BIT(3) /* TIM bit is on for PS stations */
+#define WLAN_STA_PERM BIT(4) /* permanent; do not remove entry on expiration */
+#define WLAN_STA_AUTHORIZED BIT(5) /* If 802.1X is used, this flag is
+ * controlling whether STA is authorized to
+ * send and receive non-IEEE 802.1X frames
+ */
+#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
+
+#define WLAN_RATE_1M BIT(0)
+#define WLAN_RATE_2M BIT(1)
+#define WLAN_RATE_5M5 BIT(2)
+#define WLAN_RATE_11M BIT(3)
+#define WLAN_RATE_COUNT 4
+
+/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8,
+ * but some pre-standard IEEE 802.11g products use longer elements. */
+#define WLAN_SUPP_RATES_MAX 32
+
+/* Try to increase TX rate after # successfully sent consecutive packets */
+#define WLAN_RATE_UPDATE_COUNT 50
+
+/* Decrease TX rate after # consecutive dropped packets */
+#define WLAN_RATE_DECREASE_THRESHOLD 2
+
+struct sta_info {
+ struct list_head list;
+ struct sta_info *hnext; /* next entry in hash table list */
+ atomic_t users; /* number of users (do not remove if > 0) */
+ struct proc_dir_entry *proc;
+
+ u8 addr[6];
+ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+ u32 flags;
+ u16 capability;
+ u16 listen_interval; /* or beacon_int for APs */
+ u8 supported_rates[WLAN_SUPP_RATES_MAX];
+
+ unsigned long last_auth;
+ unsigned long last_assoc;
+ unsigned long last_rx;
+ unsigned long last_tx;
+ unsigned long rx_packets, tx_packets;
+ unsigned long rx_bytes, tx_bytes;
+ struct sk_buff_head tx_buf;
+ /* FIX: timeout buffers with an expiry time somehow derived from
+ * listen_interval */
+
+ s8 last_rx_silence; /* Noise in dBm */
+ s8 last_rx_signal; /* Signal strength in dBm */
+ u8 last_rx_rate; /* TX rate in 0.1 Mbps */
+ u8 last_rx_updated; /* IWSPY's struct iw_quality::updated */
+
+ u8 tx_supp_rates; /* bit field of supported TX rates */
+ u8 tx_rate; /* current TX rate (in 0.1 Mbps) */
+ u8 tx_rate_idx; /* current TX rate (WLAN_RATE_*) */
+ u8 tx_max_rate; /* max TX rate (WLAN_RATE_*) */
+ u32 tx_count[WLAN_RATE_COUNT]; /* number of frames sent (per rate) */
+ u32 rx_count[WLAN_RATE_COUNT]; /* number of frames received (per rate)
+ */
+ u32 tx_since_last_failure;
+ u32 tx_consecutive_exc;
+
+ struct ieee80211_crypt_data *crypt;
+
+ int ap; /* whether this station is an AP */
+
+ local_info_t *local;
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ union {
+ struct {
+ char *challenge; /* shared key authentication
+ * challenge */
+ } sta;
+ struct {
+ int ssid_len;
+ unsigned char ssid[MAX_SSID_LEN + 1]; /* AP's ssid */
+ int channel;
+ unsigned long last_beacon; /* last RX beacon time */
+ } ap;
+ } u;
+
+ struct timer_list timer;
+ enum { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH } timeout_next;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+};
+
+
+#define MAX_STA_COUNT 1024
+
+/* Maximum number of AIDs to use for STAs; must be 2007 or lower
+ * (8802.11 limitation) */
+#define MAX_AID_TABLE_SIZE 128
+
+#define STA_HASH_SIZE 256
+#define STA_HASH(sta) (sta[5])
+
+
+/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY_SEC
+ * has passed since last received frame from the station, a nullfunc data
+ * frame is sent to the station. If this frame is not acknowledged and no other
+ * frames have been received, the station will be disassociated after
+ * AP_DISASSOC_DELAY. Similarily, a the station will be deauthenticated after
+ * AP_DEAUTH_DELAY. AP_TIMEOUT_RESOLUTION is the resolution that is used with
+ * max inactivity timer. */
+#define AP_MAX_INACTIVITY_SEC (5 * 60)
+#define AP_DISASSOC_DELAY (HZ)
+#define AP_DEAUTH_DELAY (HZ)
+
+/* ap_policy: whether to accept frames to/from other APs/IBSS */
+typedef enum {
+ AP_OTHER_AP_SKIP_ALL = 0,
+ AP_OTHER_AP_SAME_SSID = 1,
+ AP_OTHER_AP_ALL = 2,
+ AP_OTHER_AP_EVEN_IBSS = 3
+} ap_policy_enum;
+
+#define PRISM2_AUTH_OPEN BIT(0)
+#define PRISM2_AUTH_SHARED_KEY BIT(1)
+
+
+/* MAC address-based restrictions */
+struct mac_entry {
+ struct list_head list;
+ u8 addr[6];
+};
+
+struct mac_restrictions {
+ enum { MAC_POLICY_OPEN = 0, MAC_POLICY_ALLOW, MAC_POLICY_DENY } policy;
+ unsigned int entries;
+ struct list_head mac_list;
+ spinlock_t lock;
+};
+
+
+struct add_sta_proc_data {
+ u8 addr[ETH_ALEN];
+ struct add_sta_proc_data *next;
+};
+
+
+typedef enum { WDS_ADD, WDS_DEL } wds_oper_type;
+struct wds_oper_data {
+ wds_oper_type type;
+ u8 addr[ETH_ALEN];
+ struct wds_oper_data *next;
+};
+
+
+struct ap_data {
+ int initialized; /* whether ap_data has been initialized */
+ local_info_t *local;
+ int bridge_packets; /* send packet to associated STAs directly to the
+ * wireless media instead of higher layers in the
+ * kernel */
+ unsigned int bridged_unicast; /* number of unicast frames bridged on
+ * wireless media */
+ unsigned int bridged_multicast; /* number of non-unicast frames
+ * bridged on wireless media */
+ unsigned int tx_drop_nonassoc; /* number of unicast TX packets dropped
+ * because they were to an address that
+ * was not associated */
+ int nullfunc_ack; /* use workaround for nullfunc frame ACKs */
+
+ spinlock_t sta_table_lock;
+ int num_sta; /* number of entries in sta_list */
+ struct list_head sta_list; /* STA info list head */
+ struct sta_info *sta_hash[STA_HASH_SIZE];
+
+ struct proc_dir_entry *proc;
+
+ ap_policy_enum ap_policy;
+ unsigned int max_inactivity;
+ int autom_ap_wds;
+
+ struct mac_restrictions mac_restrictions; /* MAC-based auth */
+ int last_tx_rate;
+
+ struct work_struct add_sta_proc_queue;
+ struct add_sta_proc_data *add_sta_proc_entries;
+
+ struct work_struct wds_oper_queue;
+ struct wds_oper_data *wds_oper_entries;
+
+ u16 tx_callback_idx;
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ /* pointers to STA info; based on allocated AID or NULL if AID free
+ * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1
+ * and so on
+ */
+ struct sta_info *sta_aid[MAX_AID_TABLE_SIZE];
+
+ u16 tx_callback_auth, tx_callback_assoc, tx_callback_poll;
+
+ /* WEP operations for generating challenges to be used with shared key
+ * authentication */
+ struct ieee80211_crypto_ops *crypt;
+ void *crypt_priv;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+};
+
+
+void hostap_rx(struct net_device *dev, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats);
+void hostap_init_data(local_info_t *local);
+void hostap_init_ap_proc(local_info_t *local);
+void hostap_free_data(struct ap_data *ap);
+void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver);
+
+typedef enum {
+ AP_TX_CONTINUE, AP_TX_DROP, AP_TX_RETRY, AP_TX_BUFFERED,
+ AP_TX_CONTINUE_NOT_AUTHORIZED
+} ap_tx_ret;
+struct hostap_tx_data {
+ struct sk_buff *skb;
+ int host_encrypt;
+ struct ieee80211_crypt_data *crypt;
+ void *sta_ptr;
+};
+ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx);
+void hostap_handle_sta_release(void *ptr);
+void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb);
+int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr);
+typedef enum {
+ AP_RX_CONTINUE, AP_RX_DROP, AP_RX_EXIT, AP_RX_CONTINUE_NOT_AUTHORIZED
+} ap_rx_ret;
+ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
+ struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats,
+ int wds);
+int hostap_handle_sta_crypto(local_info_t *local, struct ieee80211_hdr *hdr,
+ struct ieee80211_crypt_data **crypt,
+ void **sta_ptr);
+int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr);
+int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr);
+int hostap_add_sta(struct ap_data *ap, u8 *sta_addr);
+int hostap_update_rx_stats(struct ap_data *ap, struct ieee80211_hdr *hdr,
+ struct hostap_80211_rx_status *rx_stats);
+void hostap_update_rates(local_info_t *local);
+void hostap_add_wds_links(local_info_t *local);
+void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type);
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap,
+ int resend);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+#endif /* HOSTAP_AP_H */
diff --git a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/hostap/hostap_common.h
new file mode 100644
index 000000000000..6f4fa9dc308f
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_common.h
@@ -0,0 +1,435 @@
+#ifndef HOSTAP_COMMON_H
+#define HOSTAP_COMMON_H
+
+#define BIT(x) (1 << (x))
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+
+/* IEEE 802.11 defines */
+
+/* Information Element IDs */
+#define WLAN_EID_SSID 0
+#define WLAN_EID_SUPP_RATES 1
+#define WLAN_EID_FH_PARAMS 2
+#define WLAN_EID_DS_PARAMS 3
+#define WLAN_EID_CF_PARAMS 4
+#define WLAN_EID_TIM 5
+#define WLAN_EID_IBSS_PARAMS 6
+#define WLAN_EID_CHALLENGE 16
+#define WLAN_EID_RSN 48
+#define WLAN_EID_GENERIC 221
+
+
+/* HFA384X Configuration RIDs */
+#define HFA384X_RID_CNFPORTTYPE 0xFC00
+#define HFA384X_RID_CNFOWNMACADDR 0xFC01
+#define HFA384X_RID_CNFDESIREDSSID 0xFC02
+#define HFA384X_RID_CNFOWNCHANNEL 0xFC03
+#define HFA384X_RID_CNFOWNSSID 0xFC04
+#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05
+#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06
+#define HFA384X_RID_CNFMAXDATALEN 0xFC07
+#define HFA384X_RID_CNFWDSADDRESS 0xFC08
+#define HFA384X_RID_CNFPMENABLED 0xFC09
+#define HFA384X_RID_CNFPMEPS 0xFC0A
+#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B
+#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C
+#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D
+#define HFA384X_RID_CNFOWNNAME 0xFC0E
+#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10
+#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */
+#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */
+#define HFA384X_RID_UNKNOWN1 0xFC20
+#define HFA384X_RID_UNKNOWN2 0xFC21
+#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23
+#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24
+#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25
+#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26
+#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27
+#define HFA384X_RID_CNFWEPFLAGS 0xFC28
+#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29
+#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A
+#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */
+#define HFA384X_RID_CNFTXCONTROL 0xFC2C
+#define HFA384X_RID_CNFROAMINGMODE 0xFC2D
+#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */
+#define HFA384X_RID_CNFRCVCRCERROR 0xFC30
+#define HFA384X_RID_CNFMMLIFE 0xFC31
+#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32
+#define HFA384X_RID_CNFBEACONINT 0xFC33
+#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */
+#define HFA384X_RID_CNFSTAPCFINFO 0xFC35
+#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37
+#define HFA384X_RID_CNFTIMCTRL 0xFC40
+#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */
+#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */
+#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */
+#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0;
+ * write only */
+#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */
+#define HFA384X_RID_GROUPADDRESSES 0xFC80
+#define HFA384X_RID_CREATEIBSS 0xFC81
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82
+#define HFA384X_RID_RTSTHRESHOLD 0xFC83
+#define HFA384X_RID_TXRATECONTROL 0xFC84
+#define HFA384X_RID_PROMISCUOUSMODE 0xFC85
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */
+#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0
+#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1
+#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2
+#define HFA384X_RID_CNFBASICRATES 0xFCB3
+#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4
+#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */
+#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */
+#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */
+#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */
+#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */
+#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */
+#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */
+#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */
+#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */
+#define HFA384X_RID_TICKTIME 0xFCE0
+#define HFA384X_RID_SCANREQUEST 0xFCE1
+#define HFA384X_RID_JOINREQUEST 0xFCE2
+#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */
+#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */
+#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */
+
+/* HFA384X Information RIDs */
+#define HFA384X_RID_MAXLOADTIME 0xFD00
+#define HFA384X_RID_DOWNLOADBUFFER 0xFD01
+#define HFA384X_RID_PRIID 0xFD02
+#define HFA384X_RID_PRISUPRANGE 0xFD03
+#define HFA384X_RID_CFIACTRANGES 0xFD04
+#define HFA384X_RID_NICSERNUM 0xFD0A
+#define HFA384X_RID_NICID 0xFD0B
+#define HFA384X_RID_MFISUPRANGE 0xFD0C
+#define HFA384X_RID_CFISUPRANGE 0xFD0D
+#define HFA384X_RID_CHANNELLIST 0xFD10
+#define HFA384X_RID_REGULATORYDOMAINS 0xFD11
+#define HFA384X_RID_TEMPTYPE 0xFD12
+#define HFA384X_RID_CIS 0xFD13
+#define HFA384X_RID_STAID 0xFD20
+#define HFA384X_RID_STASUPRANGE 0xFD21
+#define HFA384X_RID_MFIACTRANGES 0xFD22
+#define HFA384X_RID_CFIACTRANGES2 0xFD23
+#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1;
+ * only Prism2.5(?) */
+#define HFA384X_RID_PORTSTATUS 0xFD40
+#define HFA384X_RID_CURRENTSSID 0xFD41
+#define HFA384X_RID_CURRENTBSSID 0xFD42
+#define HFA384X_RID_COMMSQUALITY 0xFD43
+#define HFA384X_RID_CURRENTTXRATE 0xFD44
+#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45
+#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46
+#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47
+#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48
+#define HFA384X_RID_LONGRETRYLIMIT 0xFD49
+#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A
+#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B
+#define HFA384X_RID_CFPOLLABLE 0xFD4C
+#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D
+#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F
+#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */
+#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */
+#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */
+#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */
+#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */
+#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */
+#define HFA384X_RID_PHYTYPE 0xFDC0
+#define HFA384X_RID_CURRENTCHANNEL 0xFDC1
+#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2
+#define HFA384X_RID_CCAMODE 0xFDC3
+#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6
+#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */
+#define HFA384X_RID_BUILDSEQ 0xFFFE
+#define HFA384X_RID_FWID 0xFFFF
+
+
+struct hfa384x_comp_ident
+{
+ u16 id;
+ u16 variant;
+ u16 major;
+ u16 minor;
+} __attribute__ ((packed));
+
+#define HFA384X_COMP_ID_PRI 0x15
+#define HFA384X_COMP_ID_STA 0x1f
+#define HFA384X_COMP_ID_FW_AP 0x14b
+
+struct hfa384x_sup_range
+{
+ u16 role;
+ u16 id;
+ u16 variant;
+ u16 bottom;
+ u16 top;
+} __attribute__ ((packed));
+
+
+struct hfa384x_build_id
+{
+ u16 pri_seq;
+ u16 sec_seq;
+} __attribute__ ((packed));
+
+/* FD01 - Download Buffer */
+struct hfa384x_rid_download_buffer
+{
+ u16 page;
+ u16 offset;
+ u16 length;
+} __attribute__ ((packed));
+
+/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */
+struct hfa384x_comms_quality {
+ u16 comm_qual; /* 0 .. 92 */
+ u16 signal_level; /* 27 .. 154 */
+ u16 noise_level; /* 27 .. 154 */
+} __attribute__ ((packed));
+
+
+/* netdevice private ioctls (used, e.g., with iwpriv from user space) */
+
+/* New wireless extensions API - SET/GET convention (even ioctl numbers are
+ * root only)
+ */
+#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
+#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
+#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2)
+#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3)
+#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4)
+#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6)
+#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8)
+#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10)
+#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12)
+#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14)
+#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16)
+#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18)
+#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20)
+#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22)
+
+/* following are not in SIOCGIWPRIV list; check permission in the driver code
+ */
+#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13)
+#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14)
+
+
+/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */
+enum {
+ /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */
+ PRISM2_PARAM_TXRATECTRL = 2,
+ PRISM2_PARAM_BEACON_INT = 3,
+ PRISM2_PARAM_PSEUDO_IBSS = 4,
+ PRISM2_PARAM_ALC = 5,
+ /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */
+ PRISM2_PARAM_DUMP = 7,
+ PRISM2_PARAM_OTHER_AP_POLICY = 8,
+ PRISM2_PARAM_AP_MAX_INACTIVITY = 9,
+ PRISM2_PARAM_AP_BRIDGE_PACKETS = 10,
+ PRISM2_PARAM_DTIM_PERIOD = 11,
+ PRISM2_PARAM_AP_NULLFUNC_ACK = 12,
+ PRISM2_PARAM_MAX_WDS = 13,
+ PRISM2_PARAM_AP_AUTOM_AP_WDS = 14,
+ PRISM2_PARAM_AP_AUTH_ALGS = 15,
+ PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16,
+ PRISM2_PARAM_HOST_ENCRYPT = 17,
+ PRISM2_PARAM_HOST_DECRYPT = 18,
+ /* PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, REMOVED 2005-08-14 */
+ /* PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, REMOVED 2005-08-14 */
+ PRISM2_PARAM_HOST_ROAMING = 21,
+ PRISM2_PARAM_BCRX_STA_KEY = 22,
+ PRISM2_PARAM_IEEE_802_1X = 23,
+ PRISM2_PARAM_ANTSEL_TX = 24,
+ PRISM2_PARAM_ANTSEL_RX = 25,
+ PRISM2_PARAM_MONITOR_TYPE = 26,
+ PRISM2_PARAM_WDS_TYPE = 27,
+ PRISM2_PARAM_HOSTSCAN = 28,
+ PRISM2_PARAM_AP_SCAN = 29,
+ PRISM2_PARAM_ENH_SEC = 30,
+ PRISM2_PARAM_IO_DEBUG = 31,
+ PRISM2_PARAM_BASIC_RATES = 32,
+ PRISM2_PARAM_OPER_RATES = 33,
+ PRISM2_PARAM_HOSTAPD = 34,
+ PRISM2_PARAM_HOSTAPD_STA = 35,
+ PRISM2_PARAM_WPA = 36,
+ PRISM2_PARAM_PRIVACY_INVOKED = 37,
+ PRISM2_PARAM_TKIP_COUNTERMEASURES = 38,
+ PRISM2_PARAM_DROP_UNENCRYPTED = 39,
+ PRISM2_PARAM_SCAN_CHANNEL_MASK = 40,
+};
+
+enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1,
+ HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 };
+
+
+/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */
+enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1,
+ AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3,
+ AP_MAC_CMD_KICKALL = 4 };
+
+
+/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */
+enum {
+ PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */,
+ /* Note! Old versions of prism2_srec have a fatal error in CRC-16
+ * calculation, which will corrupt all non-volatile downloads.
+ * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to
+ * prevent use of old versions of prism2_srec for non-volatile
+ * download. */
+ PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */,
+ PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */,
+ /* Persistent versions of volatile download commands (keep firmware
+ * data in memory and automatically re-download after hw_reset */
+ PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5,
+ PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6,
+};
+
+struct prism2_download_param {
+ u32 dl_cmd;
+ u32 start_addr;
+ u32 num_areas;
+ struct prism2_download_area {
+ u32 addr; /* wlan card address */
+ u32 len;
+ void __user *ptr; /* pointer to data in user space */
+ } data[0];
+};
+
+#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072
+#define PRISM2_MAX_DOWNLOAD_LEN 262144
+
+
+/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */
+enum {
+ PRISM2_HOSTAPD_FLUSH = 1,
+ PRISM2_HOSTAPD_ADD_STA = 2,
+ PRISM2_HOSTAPD_REMOVE_STA = 3,
+ PRISM2_HOSTAPD_GET_INFO_STA = 4,
+ /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
+ PRISM2_SET_ENCRYPTION = 6,
+ PRISM2_GET_ENCRYPTION = 7,
+ PRISM2_HOSTAPD_SET_FLAGS_STA = 8,
+ PRISM2_HOSTAPD_GET_RID = 9,
+ PRISM2_HOSTAPD_SET_RID = 10,
+ PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
+ PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
+ PRISM2_HOSTAPD_MLME = 13,
+ PRISM2_HOSTAPD_SCAN_REQ = 14,
+ PRISM2_HOSTAPD_STA_CLEAR_STATS = 15,
+};
+
+#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
+#define PRISM2_HOSTAPD_RID_HDR_LEN \
+((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
+#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
+((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
+
+/* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
+ */
+#define HOSTAP_CRYPT_ALG_NAME_LEN 16
+
+
+struct prism2_hostapd_param {
+ u32 cmd;
+ u8 sta_addr[ETH_ALEN];
+ union {
+ struct {
+ u16 aid;
+ u16 capability;
+ u8 tx_supp_rates;
+ } add_sta;
+ struct {
+ u32 inactive_sec;
+ } get_info_sta;
+ struct {
+ u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
+ u32 flags;
+ u32 err;
+ u8 idx;
+ u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+ u16 key_len;
+ u8 key[0];
+ } crypt;
+ struct {
+ u32 flags_and;
+ u32 flags_or;
+ } set_flags_sta;
+ struct {
+ u16 rid;
+ u16 len;
+ u8 data[0];
+ } rid;
+ struct {
+ u8 len;
+ u8 data[0];
+ } generic_elem;
+ struct {
+#define MLME_STA_DEAUTH 0
+#define MLME_STA_DISASSOC 1
+ u16 cmd;
+ u16 reason_code;
+ } mlme;
+ struct {
+ u8 ssid_len;
+ u8 ssid[32];
+ } scan_req;
+ } u;
+};
+
+#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0)
+#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1)
+
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3
+#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5
+#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7
+
+
+#endif /* HOSTAP_COMMON_H */
diff --git a/drivers/net/wireless/hostap/hostap_config.h b/drivers/net/wireless/hostap/hostap_config.h
new file mode 100644
index 000000000000..7ed3425d08c1
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_config.h
@@ -0,0 +1,55 @@
+#ifndef HOSTAP_CONFIG_H
+#define HOSTAP_CONFIG_H
+
+#define PRISM2_VERSION "0.4.4-kernel"
+
+/* In the previous versions of Host AP driver, support for user space version
+ * of IEEE 802.11 management (hostapd) used to be disabled in the default
+ * configuration. From now on, support for hostapd is always included and it is
+ * possible to disable kernel driver version of IEEE 802.11 management with a
+ * separate define, PRISM2_NO_KERNEL_IEEE80211_MGMT. */
+/* #define PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+/* Maximum number of events handler per one interrupt */
+#define PRISM2_MAX_INTERRUPT_EVENTS 20
+
+/* Include code for downloading firmware images into volatile RAM. */
+#define PRISM2_DOWNLOAD_SUPPORT
+
+/* Allow kernel configuration to enable download support. */
+#if !defined(PRISM2_DOWNLOAD_SUPPORT) && defined(CONFIG_HOSTAP_FIRMWARE)
+#define PRISM2_DOWNLOAD_SUPPORT
+#endif
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+/* Allow writing firmware images into flash, i.e., to non-volatile storage.
+ * Before you enable this option, you should make absolutely sure that you are
+ * using prism2_srec utility that comes with THIS version of the driver!
+ * In addition, please note that it is possible to kill your card with
+ * non-volatile download if you are using incorrect image. This feature has not
+ * been fully tested, so please be careful with it. */
+/* #define PRISM2_NON_VOLATILE_DOWNLOAD */
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+/* Save low-level I/O for debugging. This should not be enabled in normal use.
+ */
+/* #define PRISM2_IO_DEBUG */
+
+/* Following defines can be used to remove unneeded parts of the driver, e.g.,
+ * to limit the size of the kernel module. Definitions can be added here in
+ * hostap_config.h or they can be added to make command with EXTRA_CFLAGS,
+ * e.g.,
+ * 'make pccard EXTRA_CFLAGS="-DPRISM2_NO_DEBUG -DPRISM2_NO_PROCFS_DEBUG"'
+ */
+
+/* Do not include debug messages into the driver */
+/* #define PRISM2_NO_DEBUG */
+
+/* Do not include /proc/net/prism2/wlan#/{registers,debug} */
+/* #define PRISM2_NO_PROCFS_DEBUG */
+
+/* Do not include station functionality (i.e., allow only Master (Host AP) mode
+ */
+/* #define PRISM2_NO_STATION_MODES */
+
+#endif /* HOSTAP_CONFIG_H */
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
new file mode 100644
index 000000000000..faa83badf0a1
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_cs.c
@@ -0,0 +1,1030 @@
+#define PRISM2_PCCARD
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include <asm/io.h>
+
+#include "hostap_wlan.h"
+
+
+static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
+static dev_info_t dev_info = "hostap_cs";
+static dev_link_t *dev_list = NULL;
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
+ "cards (PC Card).");
+MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PC Card)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PRISM2_VERSION);
+
+
+static int ignore_cis_vcc;
+module_param(ignore_cis_vcc, int, 0444);
+MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry");
+
+
+/* struct local_info::hw_priv */
+struct hostap_cs_priv {
+ dev_node_t node;
+ dev_link_t *link;
+ int sandisk_connectplus;
+};
+
+
+#ifdef PRISM2_IO_DEBUG
+
+static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
+ outb(v, dev->base_addr + a);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+ u8 v;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ v = inb(dev->base_addr + a);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
+ spin_unlock_irqrestore(&local->lock, flags);
+ return v;
+}
+
+static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
+ outw(v, dev->base_addr + a);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+ u16 v;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ v = inw(dev->base_addr + a);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
+ spin_unlock_irqrestore(&local->lock, flags);
+ return v;
+}
+
+static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
+ u8 *buf, int wc)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
+ outsw(dev->base_addr + a, buf, wc);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline void hfa384x_insw_debug(struct net_device *dev, int a,
+ u8 *buf, int wc)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
+ insw(dev->base_addr + a, buf, wc);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
+#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
+#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
+#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
+#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
+#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
+
+#else /* PRISM2_IO_DEBUG */
+
+#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
+#define HFA384X_INB(a) inb(dev->base_addr + (a))
+#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
+#define HFA384X_INW(a) inw(dev->base_addr + (a))
+#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
+#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
+
+#endif /* PRISM2_IO_DEBUG */
+
+
+static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
+ int len)
+{
+ u16 d_off;
+ u16 *pos;
+
+ d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
+ pos = (u16 *) buf;
+
+ if (len / 2)
+ HFA384X_INSW(d_off, buf, len / 2);
+ pos += len / 2;
+
+ if (len & 1)
+ *((char *) pos) = HFA384X_INB(d_off);
+
+ return 0;
+}
+
+
+static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
+{
+ u16 d_off;
+ u16 *pos;
+
+ d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
+ pos = (u16 *) buf;
+
+ if (len / 2)
+ HFA384X_OUTSW(d_off, buf, len / 2);
+ pos += len / 2;
+
+ if (len & 1)
+ HFA384X_OUTB(*((char *) pos), d_off);
+
+ return 0;
+}
+
+
+/* FIX: This might change at some point.. */
+#include "hostap_hw.c"
+
+
+
+static void prism2_detach(dev_link_t *link);
+static void prism2_release(u_long arg);
+static int prism2_event(event_t event, int priority,
+ event_callback_args_t *args);
+
+
+static int prism2_pccard_card_present(local_info_t *local)
+{
+ struct hostap_cs_priv *hw_priv = local->hw_priv;
+ if (hw_priv != NULL && hw_priv->link != NULL &&
+ ((hw_priv->link->state & (DEV_PRESENT | DEV_CONFIG)) ==
+ (DEV_PRESENT | DEV_CONFIG)))
+ return 1;
+ return 0;
+}
+
+
+/*
+ * SanDisk CompactFlash WLAN Flashcard - Product Manual v1.0
+ * Document No. 20-10-00058, January 2004
+ * http://www.sandisk.com/pdf/industrial/ProdManualCFWLANv1.0.pdf
+ */
+#define SANDISK_WLAN_ACTIVATION_OFF 0x40
+#define SANDISK_HCR_OFF 0x42
+
+
+static void sandisk_set_iobase(local_info_t *local)
+{
+ int res;
+ conf_reg_t reg;
+ struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+ reg.Function = 0;
+ reg.Action = CS_WRITE;
+ reg.Offset = 0x10; /* 0x3f0 IO base 1 */
+ reg.Value = hw_priv->link->io.BasePort1 & 0x00ff;
+ res = pcmcia_access_configuration_register(hw_priv->link->handle,
+ &reg);
+ if (res != CS_SUCCESS) {
+ printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -"
+ " res=%d\n", res);
+ }
+ udelay(10);
+
+ reg.Function = 0;
+ reg.Action = CS_WRITE;
+ reg.Offset = 0x12; /* 0x3f2 IO base 2 */
+ reg.Value = (hw_priv->link->io.BasePort1 & 0xff00) >> 8;
+ res = pcmcia_access_configuration_register(hw_priv->link->handle,
+ &reg);
+ if (res != CS_SUCCESS) {
+ printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -"
+ " res=%d\n", res);
+ }
+}
+
+
+static void sandisk_write_hcr(local_info_t *local, int hcr)
+{
+ struct net_device *dev = local->dev;
+ int i;
+
+ HFA384X_OUTB(0x80, SANDISK_WLAN_ACTIVATION_OFF);
+ udelay(50);
+ for (i = 0; i < 10; i++) {
+ HFA384X_OUTB(hcr, SANDISK_HCR_OFF);
+ }
+ udelay(55);
+ HFA384X_OUTB(0x45, SANDISK_WLAN_ACTIVATION_OFF);
+}
+
+
+static int sandisk_enable_wireless(struct net_device *dev)
+{
+ int res, ret = 0;
+ conf_reg_t reg;
+ struct hostap_interface *iface = dev->priv;
+ local_info_t *local = iface->local;
+ tuple_t tuple;
+ cisparse_t *parse = NULL;
+ u_char buf[64];
+ struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+ if (hw_priv->link->io.NumPorts1 < 0x42) {
+ /* Not enough ports to be SanDisk multi-function card */
+ ret = -ENODEV;
+ goto done;
+ }
+
+ parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
+ if (parse == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ tuple.DesiredTuple = CISTPL_MANFID;
+ tuple.Attributes = TUPLE_RETURN_COMMON;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+ if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) ||
+ pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) ||
+ pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) ||
+ parse->manfid.manf != 0xd601 || parse->manfid.card != 0x0101) {
+ /* No SanDisk manfid found */
+ ret = -ENODEV;
+ goto done;
+ }
+
+ tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
+ if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) ||
+ pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) ||
+ pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) ||
+ parse->longlink_mfc.nfn < 2) {
+ /* No multi-function links found */
+ ret = -ENODEV;
+ goto done;
+ }
+
+ printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected"
+ " - using vendor-specific initialization\n", dev->name);
+ hw_priv->sandisk_connectplus = 1;
+
+ reg.Function = 0;
+ reg.Action = CS_WRITE;
+ reg.Offset = CISREG_COR;
+ reg.Value = COR_SOFT_RESET;
+ res = pcmcia_access_configuration_register(hw_priv->link->handle,
+ &reg);
+ if (res != CS_SUCCESS) {
+ printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
+ dev->name, res);
+ goto done;
+ }
+ mdelay(5);
+
+ reg.Function = 0;
+ reg.Action = CS_WRITE;
+ reg.Offset = CISREG_COR;
+ /*
+ * Do not enable interrupts here to avoid some bogus events. Interrupts
+ * will be enabled during the first cor_sreset call.
+ */
+ reg.Value = COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | COR_FUNC_ENA;
+ res = pcmcia_access_configuration_register(hw_priv->link->handle,
+ &reg);
+ if (res != CS_SUCCESS) {
+ printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
+ dev->name, res);
+ goto done;
+ }
+ mdelay(5);
+
+ sandisk_set_iobase(local);
+
+ HFA384X_OUTB(0xc5, SANDISK_WLAN_ACTIVATION_OFF);
+ udelay(10);
+ HFA384X_OUTB(0x4b, SANDISK_WLAN_ACTIVATION_OFF);
+ udelay(10);
+
+done:
+ kfree(parse);
+ return ret;
+}
+
+
+static void prism2_pccard_cor_sreset(local_info_t *local)
+{
+ int res;
+ conf_reg_t reg;
+ struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+ if (!prism2_pccard_card_present(local))
+ return;
+
+ reg.Function = 0;
+ reg.Action = CS_READ;
+ reg.Offset = CISREG_COR;
+ reg.Value = 0;
+ res = pcmcia_access_configuration_register(hw_priv->link->handle,
+ &reg);
+ if (res != CS_SUCCESS) {
+ printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n",
+ res);
+ return;
+ }
+ printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n",
+ reg.Value);
+
+ reg.Action = CS_WRITE;
+ reg.Value |= COR_SOFT_RESET;
+ res = pcmcia_access_configuration_register(hw_priv->link->handle,
+ &reg);
+ if (res != CS_SUCCESS) {
+ printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n",
+ res);
+ return;
+ }
+
+ mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
+
+ reg.Value &= ~COR_SOFT_RESET;
+ if (hw_priv->sandisk_connectplus)
+ reg.Value |= COR_IREQ_ENA;
+ res = pcmcia_access_configuration_register(hw_priv->link->handle,
+ &reg);
+ if (res != CS_SUCCESS) {
+ printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n",
+ res);
+ return;
+ }
+
+ mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
+
+ if (hw_priv->sandisk_connectplus)
+ sandisk_set_iobase(local);
+}
+
+
+static void prism2_pccard_genesis_reset(local_info_t *local, int hcr)
+{
+ int res;
+ conf_reg_t reg;
+ int old_cor;
+ struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+ if (!prism2_pccard_card_present(local))
+ return;
+
+ if (hw_priv->sandisk_connectplus) {
+ sandisk_write_hcr(local, hcr);
+ return;
+ }
+
+ reg.Function = 0;
+ reg.Action = CS_READ;
+ reg.Offset = CISREG_COR;
+ reg.Value = 0;
+ res = pcmcia_access_configuration_register(hw_priv->link->handle,
+ &reg);
+ if (res != CS_SUCCESS) {
+ printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 1 "
+ "(%d)\n", res);
+ return;
+ }
+ printk(KERN_DEBUG "prism2_pccard_genesis_sreset: original COR %02x\n",
+ reg.Value);
+ old_cor = reg.Value;
+
+ reg.Action = CS_WRITE;
+ reg.Value |= COR_SOFT_RESET;
+ res = pcmcia_access_configuration_register(hw_priv->link->handle,
+ &reg);
+ if (res != CS_SUCCESS) {
+ printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 2 "
+ "(%d)\n", res);
+ return;
+ }
+
+ mdelay(10);
+
+ /* Setup Genesis mode */
+ reg.Action = CS_WRITE;
+ reg.Value = hcr;
+ reg.Offset = CISREG_CCSR;
+ res = pcmcia_access_configuration_register(hw_priv->link->handle,
+ &reg);
+ if (res != CS_SUCCESS) {
+ printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 3 "
+ "(%d)\n", res);
+ return;
+ }
+ mdelay(10);
+
+ reg.Action = CS_WRITE;
+ reg.Offset = CISREG_COR;
+ reg.Value = old_cor & ~COR_SOFT_RESET;
+ res = pcmcia_access_configuration_register(hw_priv->link->handle,
+ &reg);
+ if (res != CS_SUCCESS) {
+ printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 4 "
+ "(%d)\n", res);
+ return;
+ }
+
+ mdelay(10);
+}
+
+
+static int prism2_pccard_dev_open(local_info_t *local)
+{
+ struct hostap_cs_priv *hw_priv = local->hw_priv;
+ hw_priv->link->open++;
+ return 0;
+}
+
+
+static int prism2_pccard_dev_close(local_info_t *local)
+{
+ struct hostap_cs_priv *hw_priv;
+
+ if (local == NULL || local->hw_priv == NULL)
+ return 1;
+ hw_priv = local->hw_priv;
+ if (hw_priv->link == NULL)
+ return 1;
+
+ if (!hw_priv->link->open) {
+ printk(KERN_WARNING "%s: prism2_pccard_dev_close(): "
+ "link not open?!\n", local->dev->name);
+ return 1;
+ }
+
+ hw_priv->link->open--;
+
+ return 0;
+}
+
+
+static struct prism2_helper_functions prism2_pccard_funcs =
+{
+ .card_present = prism2_pccard_card_present,
+ .cor_sreset = prism2_pccard_cor_sreset,
+ .dev_open = prism2_pccard_dev_open,
+ .dev_close = prism2_pccard_dev_close,
+ .genesis_reset = prism2_pccard_genesis_reset,
+ .hw_type = HOSTAP_HW_PCCARD,
+};
+
+
+/* allocate local data and register with CardServices
+ * initialize dev_link structure, but do not configure the card yet */
+static dev_link_t *prism2_attach(void)
+{
+ dev_link_t *link;
+ client_reg_t client_reg;
+ int ret;
+
+ link = kmalloc(sizeof(dev_link_t), GFP_KERNEL);
+ if (link == NULL)
+ return NULL;
+
+ memset(link, 0, sizeof(dev_link_t));
+
+ PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info);
+ link->conf.Vcc = 33;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ /* register with CardServices */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != CS_SUCCESS) {
+ cs_error(link->handle, RegisterClient, ret);
+ prism2_detach(link);
+ return NULL;
+ }
+ return link;
+}
+
+
+static void prism2_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+
+ PDEBUG(DEBUG_FLOW, "prism2_detach\n");
+
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link)
+ break;
+ if (*linkp == NULL) {
+ printk(KERN_WARNING "%s: Attempt to detach non-existing "
+ "PCMCIA client\n", dev_info);
+ return;
+ }
+
+ if (link->state & DEV_CONFIG) {
+ prism2_release((u_long)link);
+ }
+
+ if (link->handle) {
+ int res = pcmcia_deregister_client(link->handle);
+ if (res) {
+ printk("CardService(DeregisterClient) => %d\n", res);
+ cs_error(link->handle, DeregisterClient, res);
+ }
+ }
+
+ *linkp = link->next;
+ /* release net devices */
+ if (link->priv) {
+ struct net_device *dev;
+ struct hostap_interface *iface;
+ dev = link->priv;
+ iface = netdev_priv(dev);
+ kfree(iface->local->hw_priv);
+ iface->local->hw_priv = NULL;
+ prism2_free_local_data(dev);
+ }
+ kfree(link);
+}
+
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+#define CFG_CHECK2(fn, retf) \
+do { int ret = (retf); \
+if (ret != 0) { \
+ PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", ret); \
+ cs_error(link->handle, fn, ret); \
+ goto next_entry; \
+} \
+} while (0)
+
+
+/* run after a CARD_INSERTION event is received to configure the PCMCIA
+ * socket and make the device available to the system */
+static int prism2_config(dev_link_t *link)
+{
+ struct net_device *dev;
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int ret = 1;
+ tuple_t tuple;
+ cisparse_t *parse;
+ int last_fn, last_ret;
+ u_char buf[64];
+ config_info_t conf;
+ cistpl_cftable_entry_t dflt = { 0 };
+ struct hostap_cs_priv *hw_priv;
+
+ PDEBUG(DEBUG_FLOW, "prism2_config()\n");
+
+ parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
+ hw_priv = kmalloc(sizeof(*hw_priv), GFP_KERNEL);
+ if (parse == NULL || hw_priv == NULL) {
+ kfree(parse);
+ kfree(hw_priv);
+ ret = -ENOMEM;
+ goto failed;
+ }
+ memset(hw_priv, 0, sizeof(*hw_priv));
+
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link->handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(link->handle, &tuple, parse));
+ link->conf.ConfigBase = parse->config.base;
+ link->conf.Present = parse->config.rmask[0];
+
+ CS_CHECK(GetConfigurationInfo,
+ pcmcia_get_configuration_info(link->handle, &conf));
+ PDEBUG(DEBUG_HW, "%s: %s Vcc=%d (from config)\n", dev_info,
+ ignore_cis_vcc ? "ignoring" : "setting", conf.Vcc);
+ link->conf.Vcc = conf.Vcc;
+
+ /* Look for an appropriate configuration table entry in the CIS */
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple));
+ for (;;) {
+ cistpl_cftable_entry_t *cfg = &(parse->cftable_entry);
+ CFG_CHECK2(GetTupleData,
+ pcmcia_get_tuple_data(link->handle, &tuple));
+ CFG_CHECK2(ParseTuple,
+ pcmcia_parse_tuple(link->handle, &tuple, parse));
+
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ dflt = *cfg;
+ if (cfg->index == 0)
+ goto next_entry;
+ link->conf.ConfigIndex = cfg->index;
+ PDEBUG(DEBUG_EXTRA, "Checking CFTABLE_ENTRY 0x%02X "
+ "(default 0x%02X)\n", cfg->index, dflt.index);
+
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ }
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] /
+ 10000 && !ignore_cis_vcc) {
+ PDEBUG(DEBUG_EXTRA, " Vcc mismatch - skipping"
+ " this entry\n");
+ goto next_entry;
+ }
+ } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] /
+ 10000 && !ignore_cis_vcc) {
+ PDEBUG(DEBUG_EXTRA, " Vcc (default) mismatch "
+ "- skipping this entry\n");
+ goto next_entry;
+ }
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ link->conf.Vpp1 = link->conf.Vpp2 =
+ cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
+ link->conf.Vpp1 = link->conf.Vpp2 =
+ dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+ link->conf.Attributes |= CONF_ENABLE_IRQ;
+ else if (!(link->conf.Attributes & CONF_ENABLE_IRQ)) {
+ /* At least Compaq WL200 does not have IRQInfo1 set,
+ * but it does not work without interrupts.. */
+ printk("Config has no IRQ info, but trying to enable "
+ "IRQ anyway..\n");
+ link->conf.Attributes |= CONF_ENABLE_IRQ;
+ }
+
+ /* IO window settings */
+ PDEBUG(DEBUG_EXTRA, "IO window settings: cfg->io.nwin=%d "
+ "dflt.io.nwin=%d\n",
+ cfg->io.nwin, dflt.io.nwin);
+ link->io.NumPorts1 = link->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ PDEBUG(DEBUG_EXTRA, "io->flags = 0x%04X, "
+ "io.base=0x%04x, len=%d\n", io->flags,
+ io->win[0].base, io->win[0].len);
+ if (!(io->flags & CISTPL_IO_8BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = io->flags &
+ CISTPL_IO_LINES_MASK;
+ link->io.BasePort1 = io->win[0].base;
+ link->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ link->io.Attributes2 = link->io.Attributes1;
+ link->io.BasePort2 = io->win[1].base;
+ link->io.NumPorts2 = io->win[1].len;
+ }
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ CFG_CHECK2(RequestIO,
+ pcmcia_request_io(link->handle, &link->io));
+
+ /* This configuration table entry is OK */
+ break;
+
+ next_entry:
+ CS_CHECK(GetNextTuple,
+ pcmcia_get_next_tuple(link->handle, &tuple));
+ }
+
+ /* Need to allocate net_device before requesting IRQ handler */
+ dev = prism2_init_local_data(&prism2_pccard_funcs, 0,
+ &handle_to_dev(link->handle));
+ if (dev == NULL)
+ goto failed;
+ link->priv = dev;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ local->hw_priv = hw_priv;
+ hw_priv->link = link;
+ strcpy(hw_priv->node.dev_name, dev->name);
+ link->dev = &hw_priv->node;
+
+ /*
+ * Allocate an interrupt line. Note that this does not assign a
+ * handler to the interrupt, unless the 'Handler' member of the
+ * irq structure is initialized.
+ */
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = prism2_interrupt;
+ link->irq.Instance = dev;
+ CS_CHECK(RequestIRQ,
+ pcmcia_request_irq(link->handle, &link->irq));
+ }
+
+ /*
+ * This actually configures the PCMCIA socket -- setting up
+ * the I/O windows and the interrupt mapping, and putting the
+ * card and host interface into "Memory and IO" mode.
+ */
+ CS_CHECK(RequestConfiguration,
+ pcmcia_request_configuration(link->handle, &link->conf));
+
+ dev->irq = link->irq.AssignedIRQ;
+ dev->base_addr = link->io.BasePort1;
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+ dev_info, link->conf.ConfigIndex,
+ link->conf.Vcc / 10, link->conf.Vcc % 10);
+ if (link->conf.Vpp1)
+ printk(", Vpp %d.%d", link->conf.Vpp1 / 10,
+ link->conf.Vpp1 % 10);
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ printk(", irq %d", link->irq.AssignedIRQ);
+ if (link->io.NumPorts1)
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1+link->io.NumPorts1-1);
+ if (link->io.NumPorts2)
+ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+ link->io.BasePort2+link->io.NumPorts2-1);
+ printk("\n");
+
+ link->state |= DEV_CONFIG;
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ local->shutdown = 0;
+
+ sandisk_enable_wireless(dev);
+
+ ret = prism2_hw_config(dev, 1);
+ if (!ret) {
+ ret = hostap_hw_ready(dev);
+ if (ret == 0 && local->ddev)
+ strcpy(hw_priv->node.dev_name, local->ddev->name);
+ }
+ kfree(parse);
+ return ret;
+
+ cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+
+ failed:
+ kfree(parse);
+ kfree(hw_priv);
+ prism2_release((u_long)link);
+ return ret;
+}
+
+
+static void prism2_release(u_long arg)
+{
+ dev_link_t *link = (dev_link_t *)arg;
+
+ PDEBUG(DEBUG_FLOW, "prism2_release\n");
+
+ if (link->priv) {
+ struct net_device *dev = link->priv;
+ struct hostap_interface *iface;
+
+ iface = netdev_priv(dev);
+ if (link->state & DEV_CONFIG)
+ prism2_hw_shutdown(dev, 0);
+ iface->local->shutdown = 1;
+ }
+
+ if (link->win)
+ pcmcia_release_window(link->win);
+ pcmcia_release_configuration(link->handle);
+ if (link->io.NumPorts1)
+ pcmcia_release_io(link->handle, &link->io);
+ if (link->irq.AssignedIRQ)
+ pcmcia_release_irq(link->handle, &link->irq);
+
+ link->state &= ~DEV_CONFIG;
+
+ PDEBUG(DEBUG_FLOW, "release - done\n");
+}
+
+
+static int prism2_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ struct net_device *dev = (struct net_device *) link->priv;
+
+ switch (event) {
+ case CS_EVENT_CARD_INSERTION:
+ PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_INSERTION\n", dev_info);
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ if (prism2_config(link)) {
+ PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n");
+ }
+ break;
+
+ case CS_EVENT_CARD_REMOVAL:
+ PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_REMOVAL\n", dev_info);
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ netif_stop_queue(dev);
+ netif_device_detach(dev);
+ prism2_release((u_long) link);
+ }
+ break;
+
+ case CS_EVENT_PM_SUSPEND:
+ PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info);
+ link->state |= DEV_SUSPEND;
+ /* fall through */
+
+ case CS_EVENT_RESET_PHYSICAL:
+ PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_RESET_PHYSICAL\n", dev_info);
+ if (link->state & DEV_CONFIG) {
+ if (link->open) {
+ netif_stop_queue(dev);
+ netif_device_detach(dev);
+ }
+ prism2_suspend(dev);
+ pcmcia_release_configuration(link->handle);
+ }
+ break;
+
+ case CS_EVENT_PM_RESUME:
+ PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info);
+ link->state &= ~DEV_SUSPEND;
+ /* fall through */
+
+ case CS_EVENT_CARD_RESET:
+ PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_RESET\n", dev_info);
+ if (link->state & DEV_CONFIG) {
+ pcmcia_request_configuration(link->handle,
+ &link->conf);
+ prism2_hw_shutdown(dev, 1);
+ prism2_hw_config(dev, link->open ? 0 : 1);
+ if (link->open) {
+ netif_device_attach(dev);
+ netif_start_queue(dev);
+ }
+ }
+ break;
+
+ default:
+ PDEBUG(DEBUG_EXTRA, "%s: prism2_event() - unknown event %d\n",
+ dev_info, event);
+ break;
+ }
+ return 0;
+}
+
+
+static struct pcmcia_device_id hostap_cs_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100),
+ PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300),
+ PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777),
+ PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000),
+ PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030b),
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612),
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613),
+ PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x02d2, 0x0001),
+ PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x0001),
+ PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300),
+ PCMCIA_DEVICE_MANF_CARD(0xc00f, 0x0000),
+ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005),
+ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010),
+ PCMCIA_MFC_DEVICE_PROD_ID12(0, "SanDisk", "ConnectPlus",
+ 0x7a954bd9, 0x74be00c6),
+ PCMCIA_DEVICE_PROD_ID1234(
+ "Intersil", "PRISM 2_5 PCMCIA ADAPTER", "ISL37300P",
+ "Eval-RevA",
+ 0x4b801a17, 0x6345a0bf, 0xc9049a39, 0xc23adc0e),
+ PCMCIA_DEVICE_PROD_ID123(
+ "Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02",
+ 0xe6ec52ce, 0x08649af2, 0x4b74baa0),
+ PCMCIA_DEVICE_PROD_ID123(
+ "D", "Link DWL-650 11Mbps WLAN Card", "Version 01.02",
+ 0x71b18589, 0xb6f1b0ab, 0x4b74baa0),
+ PCMCIA_DEVICE_PROD_ID123(
+ "Instant Wireless ", " Network PC CARD", "Version 01.02",
+ 0x11d901af, 0x6e9bd926, 0x4b74baa0),
+ PCMCIA_DEVICE_PROD_ID123(
+ "SMC", "SMC2632W", "Version 01.02",
+ 0xc4f8b18b, 0x474a1f2a, 0x4b74baa0),
+ PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G",
+ 0x2decece3, 0x82067c18),
+ PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card",
+ 0x54f7c49c, 0x15a75e5b),
+ PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE",
+ 0x74c5e40d, 0xdb472a18),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card",
+ 0x0733cc81, 0x0c52f395),
+ PCMCIA_DEVICE_PROD_ID12(
+ "ZoomAir 11Mbps High", "Rate wireless Networking",
+ 0x273fe3db, 0x32a1eaee),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids);
+
+
+static struct pcmcia_driver hostap_driver = {
+ .drv = {
+ .name = "hostap_cs",
+ },
+ .attach = prism2_attach,
+ .detach = prism2_detach,
+ .owner = THIS_MODULE,
+ .event = prism2_event,
+ .id_table = hostap_cs_ids,
+};
+
+static int __init init_prism2_pccard(void)
+{
+ printk(KERN_INFO "%s: %s\n", dev_info, version);
+ return pcmcia_register_driver(&hostap_driver);
+}
+
+static void __exit exit_prism2_pccard(void)
+{
+ pcmcia_unregister_driver(&hostap_driver);
+ printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
+}
+
+
+module_init(init_prism2_pccard);
+module_exit(exit_prism2_pccard);
diff --git a/drivers/net/wireless/hostap/hostap_download.c b/drivers/net/wireless/hostap/hostap_download.c
new file mode 100644
index 000000000000..ab26b52b3e76
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_download.c
@@ -0,0 +1,766 @@
+static int prism2_enable_aux_port(struct net_device *dev, int enable)
+{
+ u16 val, reg;
+ int i, tries;
+ unsigned long flags;
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->no_pri) {
+ if (enable) {
+ PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux "
+ "port is already enabled\n", dev->name);
+ }
+ return 0;
+ }
+
+ spin_lock_irqsave(&local->cmdlock, flags);
+
+ /* wait until busy bit is clear */
+ tries = HFA384X_CMD_BUSY_TIMEOUT;
+ while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) {
+ tries--;
+ udelay(1);
+ }
+ if (tries == 0) {
+ reg = HFA384X_INW(HFA384X_CMD_OFF);
+ spin_unlock_irqrestore(&local->cmdlock, flags);
+ printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n",
+ dev->name, reg);
+ return -ETIMEDOUT;
+ }
+
+ val = HFA384X_INW(HFA384X_CONTROL_OFF);
+
+ if (enable) {
+ HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF);
+ HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF);
+ HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF);
+
+ if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED)
+ printk("prism2_enable_aux_port: was not disabled!?\n");
+ val &= ~HFA384X_AUX_PORT_MASK;
+ val |= HFA384X_AUX_PORT_ENABLE;
+ } else {
+ HFA384X_OUTW(0, HFA384X_PARAM0_OFF);
+ HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
+ HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
+
+ if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED)
+ printk("prism2_enable_aux_port: was not enabled!?\n");
+ val &= ~HFA384X_AUX_PORT_MASK;
+ val |= HFA384X_AUX_PORT_DISABLE;
+ }
+ HFA384X_OUTW(val, HFA384X_CONTROL_OFF);
+
+ udelay(5);
+
+ i = 10000;
+ while (i > 0) {
+ val = HFA384X_INW(HFA384X_CONTROL_OFF);
+ val &= HFA384X_AUX_PORT_MASK;
+
+ if ((enable && val == HFA384X_AUX_PORT_ENABLED) ||
+ (!enable && val == HFA384X_AUX_PORT_DISABLED))
+ break;
+
+ udelay(10);
+ i--;
+ }
+
+ spin_unlock_irqrestore(&local->cmdlock, flags);
+
+ if (i == 0) {
+ printk("prism2_enable_aux_port(%d) timed out\n",
+ enable);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+
+static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len,
+ void *buf)
+{
+ u16 page, offset;
+ if (addr & 1 || len & 1)
+ return -1;
+
+ page = addr >> 7;
+ offset = addr & 0x7f;
+
+ HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF);
+ HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF);
+
+ udelay(5);
+
+#ifdef PRISM2_PCI
+ {
+ u16 *pos = (u16 *) buf;
+ while (len > 0) {
+ *pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF);
+ len -= 2;
+ }
+ }
+#else /* PRISM2_PCI */
+ HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2);
+#endif /* PRISM2_PCI */
+
+ return 0;
+}
+
+
+static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len,
+ void *buf)
+{
+ u16 page, offset;
+ if (addr & 1 || len & 1)
+ return -1;
+
+ page = addr >> 7;
+ offset = addr & 0x7f;
+
+ HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF);
+ HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF);
+
+ udelay(5);
+
+#ifdef PRISM2_PCI
+ {
+ u16 *pos = (u16 *) buf;
+ while (len > 0) {
+ HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF);
+ len -= 2;
+ }
+ }
+#else /* PRISM2_PCI */
+ HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2);
+#endif /* PRISM2_PCI */
+
+ return 0;
+}
+
+
+static int prism2_pda_ok(u8 *buf)
+{
+ u16 *pda = (u16 *) buf;
+ int pos;
+ u16 len, pdr;
+
+ if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff &&
+ buf[3] == 0x00)
+ return 0;
+
+ pos = 0;
+ while (pos + 1 < PRISM2_PDA_SIZE / 2) {
+ len = le16_to_cpu(pda[pos]);
+ pdr = le16_to_cpu(pda[pos + 1]);
+ if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2)
+ return 0;
+
+ if (pdr == 0x0000 && len == 2) {
+ /* PDA end found */
+ return 1;
+ }
+
+ pos += len + 1;
+ }
+
+ return 0;
+}
+
+
+static int prism2_download_aux_dump(struct net_device *dev,
+ unsigned int addr, int len, u8 *buf)
+{
+ int res;
+
+ prism2_enable_aux_port(dev, 1);
+ res = hfa384x_from_aux(dev, addr, len, buf);
+ prism2_enable_aux_port(dev, 0);
+ if (res)
+ return -1;
+
+ return 0;
+}
+
+
+static u8 * prism2_read_pda(struct net_device *dev)
+{
+ u8 *buf;
+ int res, i, found = 0;
+#define NUM_PDA_ADDRS 4
+ unsigned int pda_addr[NUM_PDA_ADDRS] = {
+ 0x7f0000 /* others than HFA3841 */,
+ 0x3f0000 /* HFA3841 */,
+ 0x390000 /* apparently used in older cards */,
+ 0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */,
+ };
+
+ buf = (u8 *) kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL);
+ if (buf == NULL)
+ return NULL;
+
+ /* Note: wlan card should be in initial state (just after init cmd)
+ * and no other operations should be performed concurrently. */
+
+ prism2_enable_aux_port(dev, 1);
+
+ for (i = 0; i < NUM_PDA_ADDRS; i++) {
+ PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x",
+ dev->name, pda_addr[i]);
+ res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf);
+ if (res)
+ continue;
+ if (res == 0 && prism2_pda_ok(buf)) {
+ PDEBUG2(DEBUG_EXTRA2, ": OK\n");
+ found = 1;
+ break;
+ } else {
+ PDEBUG2(DEBUG_EXTRA2, ": failed\n");
+ }
+ }
+
+ prism2_enable_aux_port(dev, 0);
+
+ if (!found) {
+ printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name);
+ kfree(buf);
+ buf = NULL;
+ }
+
+ return buf;
+}
+
+
+static int prism2_download_volatile(local_info_t *local,
+ struct prism2_download_data *param)
+{
+ struct net_device *dev = local->dev;
+ int ret = 0, i;
+ u16 param0, param1;
+
+ if (local->hw_downloading) {
+ printk(KERN_WARNING "%s: Already downloading - aborting new "
+ "request\n", dev->name);
+ return -1;
+ }
+
+ local->hw_downloading = 1;
+ if (local->pri_only) {
+ hfa384x_disable_interrupts(dev);
+ } else {
+ prism2_hw_shutdown(dev, 0);
+
+ if (prism2_hw_init(dev, 0)) {
+ printk(KERN_WARNING "%s: Could not initialize card for"
+ " download\n", dev->name);
+ ret = -1;
+ goto out;
+ }
+ }
+
+ if (prism2_enable_aux_port(dev, 1)) {
+ printk(KERN_WARNING "%s: Could not enable AUX port\n",
+ dev->name);
+ ret = -1;
+ goto out;
+ }
+
+ param0 = param->start_addr & 0xffff;
+ param1 = param->start_addr >> 16;
+
+ HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
+ HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
+ if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
+ (HFA384X_PROGMODE_ENABLE_VOLATILE << 8),
+ param0)) {
+ printk(KERN_WARNING "%s: Download command execution failed\n",
+ dev->name);
+ ret = -1;
+ goto out;
+ }
+
+ for (i = 0; i < param->num_areas; i++) {
+ PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n",
+ dev->name, param->data[i].len, param->data[i].addr);
+ if (hfa384x_to_aux(dev, param->data[i].addr,
+ param->data[i].len, param->data[i].data)) {
+ printk(KERN_WARNING "%s: RAM download at 0x%08x "
+ "(len=%d) failed\n", dev->name,
+ param->data[i].addr, param->data[i].len);
+ ret = -1;
+ goto out;
+ }
+ }
+
+ HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
+ HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
+ if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
+ (HFA384X_PROGMODE_DISABLE << 8), param0)) {
+ printk(KERN_WARNING "%s: Download command execution failed\n",
+ dev->name);
+ ret = -1;
+ goto out;
+ }
+ /* ProgMode disable causes the hardware to restart itself from the
+ * given starting address. Give hw some time and ACK command just in
+ * case restart did not happen. */
+ mdelay(5);
+ HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+
+ if (prism2_enable_aux_port(dev, 0)) {
+ printk(KERN_DEBUG "%s: Disabling AUX port failed\n",
+ dev->name);
+ /* continue anyway.. restart should have taken care of this */
+ }
+
+ mdelay(5);
+ local->hw_downloading = 0;
+ if (prism2_hw_config(dev, 2)) {
+ printk(KERN_WARNING "%s: Card configuration after RAM "
+ "download failed\n", dev->name);
+ ret = -1;
+ goto out;
+ }
+
+ out:
+ local->hw_downloading = 0;
+ return ret;
+}
+
+
+static int prism2_enable_genesis(local_info_t *local, int hcr)
+{
+ struct net_device *dev = local->dev;
+ u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff };
+ u8 readbuf[4];
+
+ printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n",
+ dev->name, hcr);
+ local->func->cor_sreset(local);
+ hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq);
+ local->func->genesis_reset(local, hcr);
+
+ /* Readback test */
+ hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf);
+ hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq);
+ hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf);
+
+ if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) {
+ printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n",
+ hcr);
+ return 0;
+ } else {
+ printk(KERN_DEBUG "Readback test failed, HCR 0x%02x "
+ "write %02x %02x %02x %02x read %02x %02x %02x %02x\n",
+ hcr, initseq[0], initseq[1], initseq[2], initseq[3],
+ readbuf[0], readbuf[1], readbuf[2], readbuf[3]);
+ return 1;
+ }
+}
+
+
+static int prism2_get_ram_size(local_info_t *local)
+{
+ int ret;
+
+ /* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */
+ if (prism2_enable_genesis(local, 0x1f) == 0)
+ ret = 8;
+ else if (prism2_enable_genesis(local, 0x0f) == 0)
+ ret = 16;
+ else
+ ret = -1;
+
+ /* Disable genesis mode */
+ local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17);
+
+ return ret;
+}
+
+
+static int prism2_download_genesis(local_info_t *local,
+ struct prism2_download_data *param)
+{
+ struct net_device *dev = local->dev;
+ int ram16 = 0, i;
+ int ret = 0;
+
+ if (local->hw_downloading) {
+ printk(KERN_WARNING "%s: Already downloading - aborting new "
+ "request\n", dev->name);
+ return -EBUSY;
+ }
+
+ if (!local->func->genesis_reset || !local->func->cor_sreset) {
+ printk(KERN_INFO "%s: Genesis mode downloading not supported "
+ "with this hwmodel\n", dev->name);
+ return -EOPNOTSUPP;
+ }
+
+ local->hw_downloading = 1;
+
+ if (prism2_enable_aux_port(dev, 1)) {
+ printk(KERN_DEBUG "%s: failed to enable AUX port\n",
+ dev->name);
+ ret = -EIO;
+ goto out;
+ }
+
+ if (local->sram_type == -1) {
+ /* 0x1F for x8 SRAM or 0x0F for x16 SRAM */
+ if (prism2_enable_genesis(local, 0x1f) == 0) {
+ ram16 = 0;
+ PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 "
+ "SRAM\n", dev->name);
+ } else if (prism2_enable_genesis(local, 0x0f) == 0) {
+ ram16 = 1;
+ PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 "
+ "SRAM\n", dev->name);
+ } else {
+ printk(KERN_DEBUG "%s: Could not initiate genesis "
+ "mode\n", dev->name);
+ ret = -EIO;
+ goto out;
+ }
+ } else {
+ if (prism2_enable_genesis(local, local->sram_type == 8 ?
+ 0x1f : 0x0f)) {
+ printk(KERN_DEBUG "%s: Failed to set Genesis "
+ "mode (sram_type=%d)\n", dev->name,
+ local->sram_type);
+ ret = -EIO;
+ goto out;
+ }
+ ram16 = local->sram_type != 8;
+ }
+
+ for (i = 0; i < param->num_areas; i++) {
+ PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n",
+ dev->name, param->data[i].len, param->data[i].addr);
+ if (hfa384x_to_aux(dev, param->data[i].addr,
+ param->data[i].len, param->data[i].data)) {
+ printk(KERN_WARNING "%s: RAM download at 0x%08x "
+ "(len=%d) failed\n", dev->name,
+ param->data[i].addr, param->data[i].len);
+ ret = -EIO;
+ goto out;
+ }
+ }
+
+ PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n");
+ local->func->genesis_reset(local, ram16 ? 0x07 : 0x17);
+ if (prism2_enable_aux_port(dev, 0)) {
+ printk(KERN_DEBUG "%s: Failed to disable AUX port\n",
+ dev->name);
+ }
+
+ mdelay(5);
+ local->hw_downloading = 0;
+
+ PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n");
+ /*
+ * Make sure the INIT command does not generate a command completion
+ * event by disabling interrupts.
+ */
+ hfa384x_disable_interrupts(dev);
+ if (prism2_hw_init(dev, 1)) {
+ printk(KERN_DEBUG "%s: Initialization after genesis mode "
+ "download failed\n", dev->name);
+ ret = -EIO;
+ goto out;
+ }
+
+ PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n");
+ if (prism2_hw_init2(dev, 1)) {
+ printk(KERN_DEBUG "%s: Initialization(2) after genesis mode "
+ "download failed\n", dev->name);
+ ret = -EIO;
+ goto out;
+ }
+
+ out:
+ local->hw_downloading = 0;
+ return ret;
+}
+
+
+#ifdef PRISM2_NON_VOLATILE_DOWNLOAD
+/* Note! Non-volatile downloading functionality has not yet been tested
+ * thoroughly and it may corrupt flash image and effectively kill the card that
+ * is being updated. You have been warned. */
+
+static inline int prism2_download_block(struct net_device *dev,
+ u32 addr, u8 *data,
+ u32 bufaddr, int rest_len)
+{
+ u16 param0, param1;
+ int block_len;
+
+ block_len = rest_len < 4096 ? rest_len : 4096;
+
+ param0 = addr & 0xffff;
+ param1 = addr >> 16;
+
+ HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF);
+ HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
+
+ if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
+ (HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8),
+ param0)) {
+ printk(KERN_WARNING "%s: Flash download command execution "
+ "failed\n", dev->name);
+ return -1;
+ }
+
+ if (hfa384x_to_aux(dev, bufaddr, block_len, data)) {
+ printk(KERN_WARNING "%s: flash download at 0x%08x "
+ "(len=%d) failed\n", dev->name, addr, block_len);
+ return -1;
+ }
+
+ HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
+ HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
+ if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
+ (HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8),
+ 0)) {
+ printk(KERN_WARNING "%s: Flash write command execution "
+ "failed\n", dev->name);
+ return -1;
+ }
+
+ return block_len;
+}
+
+
+static int prism2_download_nonvolatile(local_info_t *local,
+ struct prism2_download_data *dl)
+{
+ struct net_device *dev = local->dev;
+ int ret = 0, i;
+ struct {
+ u16 page;
+ u16 offset;
+ u16 len;
+ } dlbuffer;
+ u32 bufaddr;
+
+ if (local->hw_downloading) {
+ printk(KERN_WARNING "%s: Already downloading - aborting new "
+ "request\n", dev->name);
+ return -1;
+ }
+
+ ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER,
+ &dlbuffer, 6, 0);
+
+ if (ret < 0) {
+ printk(KERN_WARNING "%s: Could not read download buffer "
+ "parameters\n", dev->name);
+ goto out;
+ }
+
+ dlbuffer.page = le16_to_cpu(dlbuffer.page);
+ dlbuffer.offset = le16_to_cpu(dlbuffer.offset);
+ dlbuffer.len = le16_to_cpu(dlbuffer.len);
+
+ printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n",
+ dlbuffer.len, dlbuffer.page, dlbuffer.offset);
+
+ bufaddr = (dlbuffer.page << 7) + dlbuffer.offset;
+
+ local->hw_downloading = 1;
+
+ if (!local->pri_only) {
+ prism2_hw_shutdown(dev, 0);
+
+ if (prism2_hw_init(dev, 0)) {
+ printk(KERN_WARNING "%s: Could not initialize card for"
+ " download\n", dev->name);
+ ret = -1;
+ goto out;
+ }
+ }
+
+ hfa384x_disable_interrupts(dev);
+
+ if (prism2_enable_aux_port(dev, 1)) {
+ printk(KERN_WARNING "%s: Could not enable AUX port\n",
+ dev->name);
+ ret = -1;
+ goto out;
+ }
+
+ printk(KERN_DEBUG "%s: starting flash download\n", dev->name);
+ for (i = 0; i < dl->num_areas; i++) {
+ int rest_len = dl->data[i].len;
+ int data_off = 0;
+
+ while (rest_len > 0) {
+ int block_len;
+
+ block_len = prism2_download_block(
+ dev, dl->data[i].addr + data_off,
+ dl->data[i].data + data_off, bufaddr,
+ rest_len);
+
+ if (block_len < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ rest_len -= block_len;
+ data_off += block_len;
+ }
+ }
+
+ HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
+ HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
+ if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
+ (HFA384X_PROGMODE_DISABLE << 8), 0)) {
+ printk(KERN_WARNING "%s: Download command execution failed\n",
+ dev->name);
+ ret = -1;
+ goto out;
+ }
+
+ if (prism2_enable_aux_port(dev, 0)) {
+ printk(KERN_DEBUG "%s: Disabling AUX port failed\n",
+ dev->name);
+ /* continue anyway.. restart should have taken care of this */
+ }
+
+ mdelay(5);
+
+ local->func->hw_reset(dev);
+ local->hw_downloading = 0;
+ if (prism2_hw_config(dev, 2)) {
+ printk(KERN_WARNING "%s: Card configuration after flash "
+ "download failed\n", dev->name);
+ ret = -1;
+ } else {
+ printk(KERN_INFO "%s: Card initialized successfully after "
+ "flash download\n", dev->name);
+ }
+
+ out:
+ local->hw_downloading = 0;
+ return ret;
+}
+#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */
+
+
+static void prism2_download_free_data(struct prism2_download_data *dl)
+{
+ int i;
+
+ if (dl == NULL)
+ return;
+
+ for (i = 0; i < dl->num_areas; i++)
+ kfree(dl->data[i].data);
+ kfree(dl);
+}
+
+
+static int prism2_download(local_info_t *local,
+ struct prism2_download_param *param)
+{
+ int ret = 0;
+ int i;
+ u32 total_len = 0;
+ struct prism2_download_data *dl = NULL;
+
+ printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x "
+ "num_areas=%d\n",
+ param->dl_cmd, param->start_addr, param->num_areas);
+
+ if (param->num_areas > 100) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dl = kmalloc(sizeof(*dl) + param->num_areas *
+ sizeof(struct prism2_download_data_area), GFP_KERNEL);
+ if (dl == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ memset(dl, 0, sizeof(*dl) + param->num_areas *
+ sizeof(struct prism2_download_data_area));
+ dl->dl_cmd = param->dl_cmd;
+ dl->start_addr = param->start_addr;
+ dl->num_areas = param->num_areas;
+ for (i = 0; i < param->num_areas; i++) {
+ PDEBUG(DEBUG_EXTRA2,
+ " area %d: addr=0x%08x len=%d ptr=0x%p\n",
+ i, param->data[i].addr, param->data[i].len,
+ param->data[i].ptr);
+
+ dl->data[i].addr = param->data[i].addr;
+ dl->data[i].len = param->data[i].len;
+
+ total_len += param->data[i].len;
+ if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN ||
+ total_len > PRISM2_MAX_DOWNLOAD_LEN) {
+ ret = -E2BIG;
+ goto out;
+ }
+
+ dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL);
+ if (dl->data[i].data == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(dl->data[i].data, param->data[i].ptr,
+ param->data[i].len)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ }
+
+ switch (param->dl_cmd) {
+ case PRISM2_DOWNLOAD_VOLATILE:
+ case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT:
+ ret = prism2_download_volatile(local, dl);
+ break;
+ case PRISM2_DOWNLOAD_VOLATILE_GENESIS:
+ case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT:
+ ret = prism2_download_genesis(local, dl);
+ break;
+ case PRISM2_DOWNLOAD_NON_VOLATILE:
+#ifdef PRISM2_NON_VOLATILE_DOWNLOAD
+ ret = prism2_download_nonvolatile(local, dl);
+#else /* PRISM2_NON_VOLATILE_DOWNLOAD */
+ printk(KERN_INFO "%s: non-volatile downloading not enabled\n",
+ local->dev->name);
+ ret = -EOPNOTSUPP;
+#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */
+ break;
+ default:
+ printk(KERN_DEBUG "%s: unsupported download command %d\n",
+ local->dev->name, param->dl_cmd);
+ ret = -EINVAL;
+ break;
+ };
+
+ out:
+ if (ret == 0 && dl &&
+ param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) {
+ prism2_download_free_data(local->dl_pri);
+ local->dl_pri = dl;
+ } else if (ret == 0 && dl &&
+ param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) {
+ prism2_download_free_data(local->dl_sec);
+ local->dl_sec = dl;
+ } else
+ prism2_download_free_data(dl);
+
+ return ret;
+}
diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c
new file mode 100644
index 000000000000..e533a663deda
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_hw.c
@@ -0,0 +1,3445 @@
+/*
+ * Host AP (software wireless LAN access point) driver for
+ * Intersil Prism2/2.5/3.
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ *
+ * FIX:
+ * - there is currently no way of associating TX packets to correct wds device
+ * when TX Exc/OK event occurs, so all tx_packets and some
+ * tx_errors/tx_dropped are added to the main netdevice; using sw_support
+ * field in txdesc might be used to fix this (using Alloc event to increment
+ * tx_packets would need some further info in txfid table)
+ *
+ * Buffer Access Path (BAP) usage:
+ * Prism2 cards have two separate BAPs for accessing the card memory. These
+ * should allow concurrent access to two different frames and the driver
+ * previously used BAP0 for sending data and BAP1 for receiving data.
+ * However, there seems to be number of issues with concurrent access and at
+ * least one know hardware bug in using BAP0 and BAP1 concurrently with PCI
+ * Prism2.5. Therefore, the driver now only uses BAP0 for moving data between
+ * host and card memories. BAP0 accesses are protected with local->baplock
+ * (spin_lock_bh) to prevent concurrent use.
+ */
+
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <asm/delay.h>
+#include <asm/uaccess.h>
+
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+#include <net/ieee80211_crypt.h>
+#include <asm/irq.h>
+
+#include "hostap_80211.h"
+#include "hostap.h"
+#include "hostap_ap.h"
+
+
+/* #define final_version */
+
+static int mtu = 1500;
+module_param(mtu, int, 0444);
+MODULE_PARM_DESC(mtu, "Maximum transfer unit");
+
+static int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS };
+module_param_array(channel, int, NULL, 0444);
+MODULE_PARM_DESC(channel, "Initial channel");
+
+static char essid[33] = "test";
+module_param_string(essid, essid, sizeof(essid), 0444);
+MODULE_PARM_DESC(essid, "Host AP's ESSID");
+
+static int iw_mode[MAX_PARM_DEVICES] = { IW_MODE_MASTER, DEF_INTS };
+module_param_array(iw_mode, int, NULL, 0444);
+MODULE_PARM_DESC(iw_mode, "Initial operation mode");
+
+static int beacon_int[MAX_PARM_DEVICES] = { 100, DEF_INTS };
+module_param_array(beacon_int, int, NULL, 0444);
+MODULE_PARM_DESC(beacon_int, "Beacon interval (1 = 1024 usec)");
+
+static int dtim_period[MAX_PARM_DEVICES] = { 1, DEF_INTS };
+module_param_array(dtim_period, int, NULL, 0444);
+MODULE_PARM_DESC(dtim_period, "DTIM period");
+
+static char dev_template[16] = "wlan%d";
+module_param_string(dev_template, dev_template, sizeof(dev_template), 0444);
+MODULE_PARM_DESC(dev_template, "Prefix for network device name (default: "
+ "wlan%d)");
+
+#ifdef final_version
+#define EXTRA_EVENTS_WTERR 0
+#else
+/* check WTERR events (Wait Time-out) in development versions */
+#define EXTRA_EVENTS_WTERR HFA384X_EV_WTERR
+#endif
+
+/* Events that will be using BAP0 */
+#define HFA384X_BAP0_EVENTS \
+ (HFA384X_EV_TXEXC | HFA384X_EV_RX | HFA384X_EV_INFO | HFA384X_EV_TX)
+
+/* event mask, i.e., events that will result in an interrupt */
+#define HFA384X_EVENT_MASK \
+ (HFA384X_BAP0_EVENTS | HFA384X_EV_ALLOC | HFA384X_EV_INFDROP | \
+ HFA384X_EV_CMD | HFA384X_EV_TICK | \
+ EXTRA_EVENTS_WTERR)
+
+/* Default TX control flags: use 802.11 headers and request interrupt for
+ * failed transmits. Frames that request ACK callback, will add
+ * _TX_OK flag and _ALT_RTRY flag may be used to select different retry policy.
+ */
+#define HFA384X_TX_CTRL_FLAGS \
+ (HFA384X_TX_CTRL_802_11 | HFA384X_TX_CTRL_TX_EX)
+
+
+/* ca. 1 usec */
+#define HFA384X_CMD_BUSY_TIMEOUT 5000
+#define HFA384X_BAP_BUSY_TIMEOUT 50000
+
+/* ca. 10 usec */
+#define HFA384X_CMD_COMPL_TIMEOUT 20000
+#define HFA384X_DL_COMPL_TIMEOUT 1000000
+
+/* Wait times for initialization; yield to other processes to avoid busy
+ * waiting for long time. */
+#define HFA384X_INIT_TIMEOUT (HZ / 2) /* 500 ms */
+#define HFA384X_ALLOC_COMPL_TIMEOUT (HZ / 20) /* 50 ms */
+
+
+static void prism2_hw_reset(struct net_device *dev);
+static void prism2_check_sta_fw_version(local_info_t *local);
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+/* hostap_download.c */
+static int prism2_download_aux_dump(struct net_device *dev,
+ unsigned int addr, int len, u8 *buf);
+static u8 * prism2_read_pda(struct net_device *dev);
+static int prism2_download(local_info_t *local,
+ struct prism2_download_param *param);
+static void prism2_download_free_data(struct prism2_download_data *dl);
+static int prism2_download_volatile(local_info_t *local,
+ struct prism2_download_data *param);
+static int prism2_download_genesis(local_info_t *local,
+ struct prism2_download_data *param);
+static int prism2_get_ram_size(local_info_t *local);
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+
+
+
+#ifndef final_version
+/* magic value written to SWSUPPORT0 reg. for detecting whether card is still
+ * present */
+#define HFA384X_MAGIC 0x8A32
+#endif
+
+
+static u16 hfa384x_read_reg(struct net_device *dev, u16 reg)
+{
+ return HFA384X_INW(reg);
+}
+
+
+static void hfa384x_read_regs(struct net_device *dev,
+ struct hfa384x_regs *regs)
+{
+ regs->cmd = HFA384X_INW(HFA384X_CMD_OFF);
+ regs->evstat = HFA384X_INW(HFA384X_EVSTAT_OFF);
+ regs->offset0 = HFA384X_INW(HFA384X_OFFSET0_OFF);
+ regs->offset1 = HFA384X_INW(HFA384X_OFFSET1_OFF);
+ regs->swsupport0 = HFA384X_INW(HFA384X_SWSUPPORT0_OFF);
+}
+
+
+/**
+ * __hostap_cmd_queue_free - Free Prism2 command queue entry (private)
+ * @local: pointer to private Host AP driver data
+ * @entry: Prism2 command queue entry to be freed
+ * @del_req: request the entry to be removed
+ *
+ * Internal helper function for freeing Prism2 command queue entries.
+ * Caller must have acquired local->cmdlock before calling this function.
+ */
+static inline void __hostap_cmd_queue_free(local_info_t *local,
+ struct hostap_cmd_queue *entry,
+ int del_req)
+{
+ if (del_req) {
+ entry->del_req = 1;
+ if (!list_empty(&entry->list)) {
+ list_del_init(&entry->list);
+ local->cmd_queue_len--;
+ }
+ }
+
+ if (atomic_dec_and_test(&entry->usecnt) && entry->del_req)
+ kfree(entry);
+}
+
+
+/**
+ * hostap_cmd_queue_free - Free Prism2 command queue entry
+ * @local: pointer to private Host AP driver data
+ * @entry: Prism2 command queue entry to be freed
+ * @del_req: request the entry to be removed
+ *
+ * Free a Prism2 command queue entry.
+ */
+static inline void hostap_cmd_queue_free(local_info_t *local,
+ struct hostap_cmd_queue *entry,
+ int del_req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->cmdlock, flags);
+ __hostap_cmd_queue_free(local, entry, del_req);
+ spin_unlock_irqrestore(&local->cmdlock, flags);
+}
+
+
+/**
+ * prism2_clear_cmd_queue - Free all pending Prism2 command queue entries
+ * @local: pointer to private Host AP driver data
+ */
+static void prism2_clear_cmd_queue(local_info_t *local)
+{
+ struct list_head *ptr, *n;
+ unsigned long flags;
+ struct hostap_cmd_queue *entry;
+
+ spin_lock_irqsave(&local->cmdlock, flags);
+ list_for_each_safe(ptr, n, &local->cmd_queue) {
+ entry = list_entry(ptr, struct hostap_cmd_queue, list);
+ atomic_inc(&entry->usecnt);
+ printk(KERN_DEBUG "%s: removed pending cmd_queue entry "
+ "(type=%d, cmd=0x%04x, param0=0x%04x)\n",
+ local->dev->name, entry->type, entry->cmd,
+ entry->param0);
+ __hostap_cmd_queue_free(local, entry, 1);
+ }
+ if (local->cmd_queue_len) {
+ /* This should not happen; print debug message and clear
+ * queue length. */
+ printk(KERN_DEBUG "%s: cmd_queue_len (%d) not zero after "
+ "flush\n", local->dev->name, local->cmd_queue_len);
+ local->cmd_queue_len = 0;
+ }
+ spin_unlock_irqrestore(&local->cmdlock, flags);
+}
+
+
+/**
+ * hfa384x_cmd_issue - Issue a Prism2 command to the hardware
+ * @dev: pointer to net_device
+ * @entry: Prism2 command queue entry to be issued
+ */
+static inline int hfa384x_cmd_issue(struct net_device *dev,
+ struct hostap_cmd_queue *entry)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int tries;
+ u16 reg;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->func->card_present && !local->func->card_present(local))
+ return -ENODEV;
+
+ if (entry->issued) {
+ printk(KERN_DEBUG "%s: driver bug - re-issuing command @%p\n",
+ dev->name, entry);
+ }
+
+ /* wait until busy bit is clear; this should always be clear since the
+ * commands are serialized */
+ tries = HFA384X_CMD_BUSY_TIMEOUT;
+ while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) {
+ tries--;
+ udelay(1);
+ }
+#ifndef final_version
+ if (tries != HFA384X_CMD_BUSY_TIMEOUT) {
+ prism2_io_debug_error(dev, 1);
+ printk(KERN_DEBUG "%s: hfa384x_cmd_issue: cmd reg was busy "
+ "for %d usec\n", dev->name,
+ HFA384X_CMD_BUSY_TIMEOUT - tries);
+ }
+#endif
+ if (tries == 0) {
+ reg = HFA384X_INW(HFA384X_CMD_OFF);
+ prism2_io_debug_error(dev, 2);
+ printk(KERN_DEBUG "%s: hfa384x_cmd_issue - timeout - "
+ "reg=0x%04x\n", dev->name, reg);
+ return -ETIMEDOUT;
+ }
+
+ /* write command */
+ spin_lock_irqsave(&local->cmdlock, flags);
+ HFA384X_OUTW(entry->param0, HFA384X_PARAM0_OFF);
+ HFA384X_OUTW(entry->param1, HFA384X_PARAM1_OFF);
+ HFA384X_OUTW(entry->cmd, HFA384X_CMD_OFF);
+ entry->issued = 1;
+ spin_unlock_irqrestore(&local->cmdlock, flags);
+
+ return 0;
+}
+
+
+/**
+ * hfa384x_cmd - Issue a Prism2 command and wait (sleep) for completion
+ * @dev: pointer to net_device
+ * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
+ * @param0: value for Param0 register
+ * @param1: value for Param1 register (pointer; %NULL if not used)
+ * @resp0: pointer for Resp0 data or %NULL if Resp0 is not needed
+ *
+ * Issue given command (possibly after waiting in command queue) and sleep
+ * until the command is completed (or timed out or interrupted). This can be
+ * called only from user process context.
+ */
+static int hfa384x_cmd(struct net_device *dev, u16 cmd, u16 param0,
+ u16 *param1, u16 *resp0)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int err, res, issue, issued = 0;
+ unsigned long flags;
+ struct hostap_cmd_queue *entry;
+ DECLARE_WAITQUEUE(wait, current);
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (in_interrupt()) {
+ printk(KERN_DEBUG "%s: hfa384x_cmd called from interrupt "
+ "context\n", dev->name);
+ return -1;
+ }
+
+ if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN) {
+ printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n",
+ dev->name);
+ return -1;
+ }
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ entry = (struct hostap_cmd_queue *)
+ kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (entry == NULL) {
+ printk(KERN_DEBUG "%s: hfa384x_cmd - kmalloc failed\n",
+ dev->name);
+ return -ENOMEM;
+ }
+ memset(entry, 0, sizeof(*entry));
+ atomic_set(&entry->usecnt, 1);
+ entry->type = CMD_SLEEP;
+ entry->cmd = cmd;
+ entry->param0 = param0;
+ if (param1)
+ entry->param1 = *param1;
+ init_waitqueue_head(&entry->compl);
+
+ /* prepare to wait for command completion event, but do not sleep yet
+ */
+ add_wait_queue(&entry->compl, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&local->cmdlock, flags);
+ issue = list_empty(&local->cmd_queue);
+ if (issue)
+ entry->issuing = 1;
+ list_add_tail(&entry->list, &local->cmd_queue);
+ local->cmd_queue_len++;
+ spin_unlock_irqrestore(&local->cmdlock, flags);
+
+ err = 0;
+ if (!issue)
+ goto wait_completion;
+
+ if (signal_pending(current))
+ err = -EINTR;
+
+ if (!err) {
+ if (hfa384x_cmd_issue(dev, entry))
+ err = -ETIMEDOUT;
+ else
+ issued = 1;
+ }
+
+ wait_completion:
+ if (!err && entry->type != CMD_COMPLETED) {
+ /* sleep until command is completed or timed out */
+ res = schedule_timeout(2 * HZ);
+ } else
+ res = -1;
+
+ if (!err && signal_pending(current))
+ err = -EINTR;
+
+ if (err && issued) {
+ /* the command was issued, so a CmdCompl event should occur
+ * soon; however, there's a pending signal and
+ * schedule_timeout() would be interrupted; wait a short period
+ * of time to avoid removing entry from the list before
+ * CmdCompl event */
+ udelay(300);
+ }
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&entry->compl, &wait);
+
+ /* If entry->list is still in the list, it must be removed
+ * first and in this case prism2_cmd_ev() does not yet have
+ * local reference to it, and the data can be kfree()'d
+ * here. If the command completion event is still generated,
+ * it will be assigned to next (possibly) pending command, but
+ * the driver will reset the card anyway due to timeout
+ *
+ * If the entry is not in the list prism2_cmd_ev() has a local
+ * reference to it, but keeps cmdlock as long as the data is
+ * needed, so the data can be kfree()'d here. */
+
+ /* FIX: if the entry->list is in the list, it has not been completed
+ * yet, so removing it here is somewhat wrong.. this could cause
+ * references to freed memory and next list_del() causing NULL pointer
+ * dereference.. it would probably be better to leave the entry in the
+ * list and the list should be emptied during hw reset */
+
+ spin_lock_irqsave(&local->cmdlock, flags);
+ if (!list_empty(&entry->list)) {
+ printk(KERN_DEBUG "%s: hfa384x_cmd: entry still in list? "
+ "(entry=%p, type=%d, res=%d)\n", dev->name, entry,
+ entry->type, res);
+ list_del_init(&entry->list);
+ local->cmd_queue_len--;
+ }
+ spin_unlock_irqrestore(&local->cmdlock, flags);
+
+ if (err) {
+ printk(KERN_DEBUG "%s: hfa384x_cmd: interrupted; err=%d\n",
+ dev->name, err);
+ res = err;
+ goto done;
+ }
+
+ if (entry->type != CMD_COMPLETED) {
+ u16 reg = HFA384X_INW(HFA384X_EVSTAT_OFF);
+ printk(KERN_DEBUG "%s: hfa384x_cmd: command was not "
+ "completed (res=%d, entry=%p, type=%d, cmd=0x%04x, "
+ "param0=0x%04x, EVSTAT=%04x INTEN=%04x)\n", dev->name,
+ res, entry, entry->type, entry->cmd, entry->param0, reg,
+ HFA384X_INW(HFA384X_INTEN_OFF));
+ if (reg & HFA384X_EV_CMD) {
+ /* Command completion event is pending, but the
+ * interrupt was not delivered - probably an issue
+ * with pcmcia-cs configuration. */
+ printk(KERN_WARNING "%s: interrupt delivery does not "
+ "seem to work\n", dev->name);
+ }
+ prism2_io_debug_error(dev, 3);
+ res = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (resp0 != NULL)
+ *resp0 = entry->resp0;
+#ifndef final_version
+ if (entry->res) {
+ printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x, "
+ "resp0=0x%04x\n",
+ dev->name, cmd, entry->res, entry->resp0);
+ }
+#endif /* final_version */
+
+ res = entry->res;
+ done:
+ hostap_cmd_queue_free(local, entry, 1);
+ return res;
+}
+
+
+/**
+ * hfa384x_cmd_callback - Issue a Prism2 command; callback when completed
+ * @dev: pointer to net_device
+ * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
+ * @param0: value for Param0 register
+ * @callback: command completion callback function (%NULL = no callback)
+ * @context: context data to be given to the callback function
+ *
+ * Issue given command (possibly after waiting in command queue) and use
+ * callback function to indicate command completion. This can be called both
+ * from user and interrupt context. The callback function will be called in
+ * hardware IRQ context. It can be %NULL, when no function is called when
+ * command is completed.
+ */
+static int hfa384x_cmd_callback(struct net_device *dev, u16 cmd, u16 param0,
+ void (*callback)(struct net_device *dev,
+ long context, u16 resp0,
+ u16 status),
+ long context)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int issue, ret;
+ unsigned long flags;
+ struct hostap_cmd_queue *entry;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN + 2) {
+ printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n",
+ dev->name);
+ return -1;
+ }
+
+ entry = (struct hostap_cmd_queue *)
+ kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (entry == NULL) {
+ printk(KERN_DEBUG "%s: hfa384x_cmd_callback - kmalloc "
+ "failed\n", dev->name);
+ return -ENOMEM;
+ }
+ memset(entry, 0, sizeof(*entry));
+ atomic_set(&entry->usecnt, 1);
+ entry->type = CMD_CALLBACK;
+ entry->cmd = cmd;
+ entry->param0 = param0;
+ entry->callback = callback;
+ entry->context = context;
+
+ spin_lock_irqsave(&local->cmdlock, flags);
+ issue = list_empty(&local->cmd_queue);
+ if (issue)
+ entry->issuing = 1;
+ list_add_tail(&entry->list, &local->cmd_queue);
+ local->cmd_queue_len++;
+ spin_unlock_irqrestore(&local->cmdlock, flags);
+
+ if (issue && hfa384x_cmd_issue(dev, entry))
+ ret = -ETIMEDOUT;
+ else
+ ret = 0;
+
+ hostap_cmd_queue_free(local, entry, ret);
+
+ return ret;
+}
+
+
+/**
+ * __hfa384x_cmd_no_wait - Issue a Prism2 command (private)
+ * @dev: pointer to net_device
+ * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
+ * @param0: value for Param0 register
+ * @io_debug_num: I/O debug error number
+ *
+ * Shared helper function for hfa384x_cmd_wait() and hfa384x_cmd_no_wait().
+ */
+static int __hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, u16 param0,
+ int io_debug_num)
+{
+ int tries;
+ u16 reg;
+
+ /* wait until busy bit is clear; this should always be clear since the
+ * commands are serialized */
+ tries = HFA384X_CMD_BUSY_TIMEOUT;
+ while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) {
+ tries--;
+ udelay(1);
+ }
+ if (tries == 0) {
+ reg = HFA384X_INW(HFA384X_CMD_OFF);
+ prism2_io_debug_error(dev, io_debug_num);
+ printk(KERN_DEBUG "%s: __hfa384x_cmd_no_wait(%d) - timeout - "
+ "reg=0x%04x\n", dev->name, io_debug_num, reg);
+ return -ETIMEDOUT;
+ }
+
+ /* write command */
+ HFA384X_OUTW(param0, HFA384X_PARAM0_OFF);
+ HFA384X_OUTW(cmd, HFA384X_CMD_OFF);
+
+ return 0;
+}
+
+
+/**
+ * hfa384x_cmd_wait - Issue a Prism2 command and busy wait for completion
+ * @dev: pointer to net_device
+ * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
+ * @param0: value for Param0 register
+ */
+static int hfa384x_cmd_wait(struct net_device *dev, u16 cmd, u16 param0)
+{
+ int res, tries;
+ u16 reg;
+
+ res = __hfa384x_cmd_no_wait(dev, cmd, param0, 4);
+ if (res)
+ return res;
+
+ /* wait for command completion */
+ if ((cmd & HFA384X_CMDCODE_MASK) == HFA384X_CMDCODE_DOWNLOAD)
+ tries = HFA384X_DL_COMPL_TIMEOUT;
+ else
+ tries = HFA384X_CMD_COMPL_TIMEOUT;
+
+ while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) &&
+ tries > 0) {
+ tries--;
+ udelay(10);
+ }
+ if (tries == 0) {
+ reg = HFA384X_INW(HFA384X_EVSTAT_OFF);
+ prism2_io_debug_error(dev, 5);
+ printk(KERN_DEBUG "%s: hfa384x_cmd_wait - timeout2 - "
+ "reg=0x%04x\n", dev->name, reg);
+ return -ETIMEDOUT;
+ }
+
+ res = (HFA384X_INW(HFA384X_STATUS_OFF) &
+ (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | BIT(9) |
+ BIT(8))) >> 8;
+#ifndef final_version
+ if (res) {
+ printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x\n",
+ dev->name, cmd, res);
+ }
+#endif
+
+ HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+
+ return res;
+}
+
+
+/**
+ * hfa384x_cmd_no_wait - Issue a Prism2 command; do not wait for completion
+ * @dev: pointer to net_device
+ * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
+ * @param0: value for Param0 register
+ */
+static inline int hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd,
+ u16 param0)
+{
+ return __hfa384x_cmd_no_wait(dev, cmd, param0, 6);
+}
+
+
+/**
+ * prism2_cmd_ev - Prism2 command completion event handler
+ * @dev: pointer to net_device
+ *
+ * Interrupt handler for command completion events. Called by the main
+ * interrupt handler in hardware IRQ context. Read Resp0 and status registers
+ * from the hardware and ACK the event. Depending on the issued command type
+ * either wake up the sleeping process that is waiting for command completion
+ * or call the callback function. Issue the next command, if one is pending.
+ */
+static void prism2_cmd_ev(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct hostap_cmd_queue *entry = NULL;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ spin_lock(&local->cmdlock);
+ if (!list_empty(&local->cmd_queue)) {
+ entry = list_entry(local->cmd_queue.next,
+ struct hostap_cmd_queue, list);
+ atomic_inc(&entry->usecnt);
+ list_del_init(&entry->list);
+ local->cmd_queue_len--;
+
+ if (!entry->issued) {
+ printk(KERN_DEBUG "%s: Command completion event, but "
+ "cmd not issued\n", dev->name);
+ __hostap_cmd_queue_free(local, entry, 1);
+ entry = NULL;
+ }
+ }
+ spin_unlock(&local->cmdlock);
+
+ if (!entry) {
+ HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+ printk(KERN_DEBUG "%s: Command completion event, but no "
+ "pending commands\n", dev->name);
+ return;
+ }
+
+ entry->resp0 = HFA384X_INW(HFA384X_RESP0_OFF);
+ entry->res = (HFA384X_INW(HFA384X_STATUS_OFF) &
+ (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) |
+ BIT(9) | BIT(8))) >> 8;
+ HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+
+ /* TODO: rest of the CmdEv handling could be moved to tasklet */
+ if (entry->type == CMD_SLEEP) {
+ entry->type = CMD_COMPLETED;
+ wake_up_interruptible(&entry->compl);
+ } else if (entry->type == CMD_CALLBACK) {
+ if (entry->callback)
+ entry->callback(dev, entry->context, entry->resp0,
+ entry->res);
+ } else {
+ printk(KERN_DEBUG "%s: Invalid command completion type %d\n",
+ dev->name, entry->type);
+ }
+ hostap_cmd_queue_free(local, entry, 1);
+
+ /* issue next command, if pending */
+ entry = NULL;
+ spin_lock(&local->cmdlock);
+ if (!list_empty(&local->cmd_queue)) {
+ entry = list_entry(local->cmd_queue.next,
+ struct hostap_cmd_queue, list);
+ if (entry->issuing) {
+ /* hfa384x_cmd() has already started issuing this
+ * command, so do not start here */
+ entry = NULL;
+ }
+ if (entry)
+ atomic_inc(&entry->usecnt);
+ }
+ spin_unlock(&local->cmdlock);
+
+ if (entry) {
+ /* issue next command; if command issuing fails, remove the
+ * entry from cmd_queue */
+ int res = hfa384x_cmd_issue(dev, entry);
+ spin_lock(&local->cmdlock);
+ __hostap_cmd_queue_free(local, entry, res);
+ spin_unlock(&local->cmdlock);
+ }
+}
+
+
+static inline int hfa384x_wait_offset(struct net_device *dev, u16 o_off)
+{
+ int tries = HFA384X_BAP_BUSY_TIMEOUT;
+ int res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY;
+
+ while (res && tries > 0) {
+ tries--;
+ udelay(1);
+ res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY;
+ }
+ return res;
+}
+
+
+/* Offset must be even */
+static int hfa384x_setup_bap(struct net_device *dev, u16 bap, u16 id,
+ int offset)
+{
+ u16 o_off, s_off;
+ int ret = 0;
+
+ if (offset % 2 || bap > 1)
+ return -EINVAL;
+
+ if (bap == BAP1) {
+ o_off = HFA384X_OFFSET1_OFF;
+ s_off = HFA384X_SELECT1_OFF;
+ } else {
+ o_off = HFA384X_OFFSET0_OFF;
+ s_off = HFA384X_SELECT0_OFF;
+ }
+
+ if (hfa384x_wait_offset(dev, o_off)) {
+ prism2_io_debug_error(dev, 7);
+ printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout before\n",
+ dev->name);
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ HFA384X_OUTW(id, s_off);
+ HFA384X_OUTW(offset, o_off);
+
+ if (hfa384x_wait_offset(dev, o_off)) {
+ prism2_io_debug_error(dev, 8);
+ printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout after\n",
+ dev->name);
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+#ifndef final_version
+ if (HFA384X_INW(o_off) & HFA384X_OFFSET_ERR) {
+ prism2_io_debug_error(dev, 9);
+ printk(KERN_DEBUG "%s: hfa384x_setup_bap - offset error "
+ "(%d,0x04%x,%d); reg=0x%04x\n",
+ dev->name, bap, id, offset, HFA384X_INW(o_off));
+ ret = -EINVAL;
+ }
+#endif
+
+ out:
+ return ret;
+}
+
+
+static int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len,
+ int exact_len)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int res, rlen = 0;
+ struct hfa384x_rid_hdr rec;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->no_pri) {
+ printk(KERN_DEBUG "%s: cannot get RID %04x (len=%d) - no PRI "
+ "f/w\n", dev->name, rid, len);
+ return -ENOTTY; /* Well.. not really correct, but return
+ * something unique enough.. */
+ }
+
+ if ((local->func->card_present && !local->func->card_present(local)) ||
+ local->hw_downloading)
+ return -ENODEV;
+
+ res = down_interruptible(&local->rid_bap_sem);
+ if (res)
+ return res;
+
+ res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS, rid, NULL, NULL);
+ if (res) {
+ printk(KERN_DEBUG "%s: hfa384x_get_rid: CMDCODE_ACCESS failed "
+ "(res=%d, rid=%04x, len=%d)\n",
+ dev->name, res, rid, len);
+ up(&local->rid_bap_sem);
+ return res;
+ }
+
+ spin_lock_bh(&local->baplock);
+
+ res = hfa384x_setup_bap(dev, BAP0, rid, 0);
+ if (!res)
+ res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec));
+
+ if (le16_to_cpu(rec.len) == 0) {
+ /* RID not available */
+ res = -ENODATA;
+ }
+
+ rlen = (le16_to_cpu(rec.len) - 1) * 2;
+ if (!res && exact_len && rlen != len) {
+ printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: "
+ "rid=0x%04x, len=%d (expected %d)\n",
+ dev->name, rid, rlen, len);
+ res = -ENODATA;
+ }
+
+ if (!res)
+ res = hfa384x_from_bap(dev, BAP0, buf, len);
+
+ spin_unlock_bh(&local->baplock);
+ up(&local->rid_bap_sem);
+
+ if (res) {
+ if (res != -ENODATA)
+ printk(KERN_DEBUG "%s: hfa384x_get_rid (rid=%04x, "
+ "len=%d) - failed - res=%d\n", dev->name, rid,
+ len, res);
+ if (res == -ETIMEDOUT)
+ prism2_hw_reset(dev);
+ return res;
+ }
+
+ return rlen;
+}
+
+
+static int hfa384x_set_rid(struct net_device *dev, u16 rid, void *buf, int len)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct hfa384x_rid_hdr rec;
+ int res;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->no_pri) {
+ printk(KERN_DEBUG "%s: cannot set RID %04x (len=%d) - no PRI "
+ "f/w\n", dev->name, rid, len);
+ return -ENOTTY; /* Well.. not really correct, but return
+ * something unique enough.. */
+ }
+
+ if ((local->func->card_present && !local->func->card_present(local)) ||
+ local->hw_downloading)
+ return -ENODEV;
+
+ rec.rid = cpu_to_le16(rid);
+ /* RID len in words and +1 for rec.rid */
+ rec.len = cpu_to_le16(len / 2 + len % 2 + 1);
+
+ res = down_interruptible(&local->rid_bap_sem);
+ if (res)
+ return res;
+
+ spin_lock_bh(&local->baplock);
+ res = hfa384x_setup_bap(dev, BAP0, rid, 0);
+ if (!res)
+ res = hfa384x_to_bap(dev, BAP0, &rec, sizeof(rec));
+ if (!res)
+ res = hfa384x_to_bap(dev, BAP0, buf, len);
+ spin_unlock_bh(&local->baplock);
+
+ if (res) {
+ printk(KERN_DEBUG "%s: hfa384x_set_rid (rid=%04x, len=%d) - "
+ "failed - res=%d\n", dev->name, rid, len, res);
+ up(&local->rid_bap_sem);
+ return res;
+ }
+
+ res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS_WRITE, rid, NULL, NULL);
+ up(&local->rid_bap_sem);
+ if (res) {
+ printk(KERN_DEBUG "%s: hfa384x_set_rid: CMDCODE_ACCESS_WRITE "
+ "failed (res=%d, rid=%04x, len=%d)\n",
+ dev->name, res, rid, len);
+ return res;
+ }
+
+ if (res == -ETIMEDOUT)
+ prism2_hw_reset(dev);
+
+ return res;
+}
+
+
+static void hfa384x_disable_interrupts(struct net_device *dev)
+{
+ /* disable interrupts and clear event status */
+ HFA384X_OUTW(0, HFA384X_INTEN_OFF);
+ HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
+}
+
+
+static void hfa384x_enable_interrupts(struct net_device *dev)
+{
+ /* ack pending events and enable interrupts from selected events */
+ HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
+ HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF);
+}
+
+
+static void hfa384x_events_no_bap0(struct net_device *dev)
+{
+ HFA384X_OUTW(HFA384X_EVENT_MASK & ~HFA384X_BAP0_EVENTS,
+ HFA384X_INTEN_OFF);
+}
+
+
+static void hfa384x_events_all(struct net_device *dev)
+{
+ HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF);
+}
+
+
+static void hfa384x_events_only_cmd(struct net_device *dev)
+{
+ HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_INTEN_OFF);
+}
+
+
+static u16 hfa384x_allocate_fid(struct net_device *dev, int len)
+{
+ u16 fid;
+ unsigned long delay;
+
+ /* FIX: this could be replace with hfa384x_cmd() if the Alloc event
+ * below would be handled like CmdCompl event (sleep here, wake up from
+ * interrupt handler */
+ if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_ALLOC, len)) {
+ printk(KERN_DEBUG "%s: cannot allocate fid, len=%d\n",
+ dev->name, len);
+ return 0xffff;
+ }
+
+ delay = jiffies + HFA384X_ALLOC_COMPL_TIMEOUT;
+ while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC) &&
+ time_before(jiffies, delay))
+ yield();
+ if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC)) {
+ printk("%s: fid allocate, len=%d - timeout\n", dev->name, len);
+ return 0xffff;
+ }
+
+ fid = HFA384X_INW(HFA384X_ALLOCFID_OFF);
+ HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF);
+
+ return fid;
+}
+
+
+static int prism2_reset_port(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int res;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (!local->dev_enabled)
+ return 0;
+
+ res = hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0,
+ NULL, NULL);
+ if (res)
+ printk(KERN_DEBUG "%s: reset port failed to disable port\n",
+ dev->name);
+ else {
+ res = hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0,
+ NULL, NULL);
+ if (res)
+ printk(KERN_DEBUG "%s: reset port failed to enable "
+ "port\n", dev->name);
+ }
+
+ /* It looks like at least some STA firmware versions reset
+ * fragmentation threshold back to 2346 after enable command. Restore
+ * the configured value, if it differs from this default. */
+ if (local->fragm_threshold != 2346 &&
+ hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD,
+ local->fragm_threshold)) {
+ printk(KERN_DEBUG "%s: failed to restore fragmentation "
+ "threshold (%d) after Port0 enable\n",
+ dev->name, local->fragm_threshold);
+ }
+
+ return res;
+}
+
+
+static int prism2_get_version_info(struct net_device *dev, u16 rid,
+ const char *txt)
+{
+ struct hfa384x_comp_ident comp;
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->no_pri) {
+ /* PRI f/w not yet available - cannot read RIDs */
+ return -1;
+ }
+ if (hfa384x_get_rid(dev, rid, &comp, sizeof(comp), 1) < 0) {
+ printk(KERN_DEBUG "Could not get RID for component %s\n", txt);
+ return -1;
+ }
+
+ printk(KERN_INFO "%s: %s: id=0x%02x v%d.%d.%d\n", dev->name, txt,
+ __le16_to_cpu(comp.id), __le16_to_cpu(comp.major),
+ __le16_to_cpu(comp.minor), __le16_to_cpu(comp.variant));
+ return 0;
+}
+
+
+static int prism2_setup_rids(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 tmp;
+ int ret = 0;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ hostap_set_word(dev, HFA384X_RID_TICKTIME, 2000);
+
+ if (!local->fw_ap) {
+ tmp = hostap_get_porttype(local);
+ ret = hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, tmp);
+ if (ret) {
+ printk("%s: Port type setting to %d failed\n",
+ dev->name, tmp);
+ goto fail;
+ }
+ }
+
+ /* Setting SSID to empty string seems to kill the card in Host AP mode
+ */
+ if (local->iw_mode != IW_MODE_MASTER || local->essid[0] != '\0') {
+ ret = hostap_set_string(dev, HFA384X_RID_CNFOWNSSID,
+ local->essid);
+ if (ret) {
+ printk("%s: AP own SSID setting failed\n", dev->name);
+ goto fail;
+ }
+ }
+
+ ret = hostap_set_word(dev, HFA384X_RID_CNFMAXDATALEN,
+ PRISM2_DATA_MAXLEN);
+ if (ret) {
+ printk("%s: MAC data length setting to %d failed\n",
+ dev->name, PRISM2_DATA_MAXLEN);
+ goto fail;
+ }
+
+ if (hfa384x_get_rid(dev, HFA384X_RID_CHANNELLIST, &tmp, 2, 1) < 0) {
+ printk("%s: Channel list read failed\n", dev->name);
+ ret = -EINVAL;
+ goto fail;
+ }
+ local->channel_mask = __le16_to_cpu(tmp);
+
+ if (local->channel < 1 || local->channel > 14 ||
+ !(local->channel_mask & (1 << (local->channel - 1)))) {
+ printk(KERN_WARNING "%s: Channel setting out of range "
+ "(%d)!\n", dev->name, local->channel);
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ ret = hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel);
+ if (ret) {
+ printk("%s: Channel setting to %d failed\n",
+ dev->name, local->channel);
+ goto fail;
+ }
+
+ ret = hostap_set_word(dev, HFA384X_RID_CNFBEACONINT,
+ local->beacon_int);
+ if (ret) {
+ printk("%s: Beacon interval setting to %d failed\n",
+ dev->name, local->beacon_int);
+ /* this may fail with Symbol/Lucent firmware */
+ if (ret == -ETIMEDOUT)
+ goto fail;
+ }
+
+ ret = hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD,
+ local->dtim_period);
+ if (ret) {
+ printk("%s: DTIM period setting to %d failed\n",
+ dev->name, local->dtim_period);
+ /* this may fail with Symbol/Lucent firmware */
+ if (ret == -ETIMEDOUT)
+ goto fail;
+ }
+
+ ret = hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE,
+ local->is_promisc);
+ if (ret)
+ printk(KERN_INFO "%s: Setting promiscuous mode (%d) failed\n",
+ dev->name, local->is_promisc);
+
+ if (!local->fw_ap) {
+ ret = hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID,
+ local->essid);
+ if (ret) {
+ printk("%s: Desired SSID setting failed\n", dev->name);
+ goto fail;
+ }
+ }
+
+ /* Setup TXRateControl, defaults to allow use of 1, 2, 5.5, and
+ * 11 Mbps in automatic TX rate fallback and 1 and 2 Mbps as basic
+ * rates */
+ if (local->tx_rate_control == 0) {
+ local->tx_rate_control =
+ HFA384X_RATES_1MBPS |
+ HFA384X_RATES_2MBPS |
+ HFA384X_RATES_5MBPS |
+ HFA384X_RATES_11MBPS;
+ }
+ if (local->basic_rates == 0)
+ local->basic_rates = HFA384X_RATES_1MBPS | HFA384X_RATES_2MBPS;
+
+ if (!local->fw_ap) {
+ ret = hostap_set_word(dev, HFA384X_RID_TXRATECONTROL,
+ local->tx_rate_control);
+ if (ret) {
+ printk("%s: TXRateControl setting to %d failed\n",
+ dev->name, local->tx_rate_control);
+ goto fail;
+ }
+
+ ret = hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES,
+ local->tx_rate_control);
+ if (ret) {
+ printk("%s: cnfSupportedRates setting to %d failed\n",
+ dev->name, local->tx_rate_control);
+ }
+
+ ret = hostap_set_word(dev, HFA384X_RID_CNFBASICRATES,
+ local->basic_rates);
+ if (ret) {
+ printk("%s: cnfBasicRates setting to %d failed\n",
+ dev->name, local->basic_rates);
+ }
+
+ ret = hostap_set_word(dev, HFA384X_RID_CREATEIBSS, 1);
+ if (ret) {
+ printk("%s: Create IBSS setting to 1 failed\n",
+ dev->name);
+ }
+ }
+
+ if (local->name_set)
+ (void) hostap_set_string(dev, HFA384X_RID_CNFOWNNAME,
+ local->name);
+
+ if (hostap_set_encryption(local)) {
+ printk(KERN_INFO "%s: could not configure encryption\n",
+ dev->name);
+ }
+
+ (void) hostap_set_antsel(local);
+
+ if (hostap_set_roaming(local)) {
+ printk(KERN_INFO "%s: could not set host roaming\n",
+ dev->name);
+ }
+
+ if (local->sta_fw_ver >= PRISM2_FW_VER(1,6,3) &&
+ hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, local->enh_sec))
+ printk(KERN_INFO "%s: cnfEnhSecurity setting to 0x%x failed\n",
+ dev->name, local->enh_sec);
+
+ /* 32-bit tallies were added in STA f/w 0.8.0, but they were apparently
+ * not working correctly (last seven counters report bogus values).
+ * This has been fixed in 0.8.2, so enable 32-bit tallies only
+ * beginning with that firmware version. Another bug fix for 32-bit
+ * tallies in 1.4.0; should 16-bit tallies be used for some other
+ * versions, too? */
+ if (local->sta_fw_ver >= PRISM2_FW_VER(0,8,2)) {
+ if (hostap_set_word(dev, HFA384X_RID_CNFTHIRTY2TALLY, 1)) {
+ printk(KERN_INFO "%s: cnfThirty2Tally setting "
+ "failed\n", dev->name);
+ local->tallies32 = 0;
+ } else
+ local->tallies32 = 1;
+ } else
+ local->tallies32 = 0;
+
+ hostap_set_auth_algs(local);
+
+ if (hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD,
+ local->fragm_threshold)) {
+ printk(KERN_INFO "%s: setting FragmentationThreshold to %d "
+ "failed\n", dev->name, local->fragm_threshold);
+ }
+
+ if (hostap_set_word(dev, HFA384X_RID_RTSTHRESHOLD,
+ local->rts_threshold)) {
+ printk(KERN_INFO "%s: setting RTSThreshold to %d failed\n",
+ dev->name, local->rts_threshold);
+ }
+
+ if (local->manual_retry_count >= 0 &&
+ hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT,
+ local->manual_retry_count)) {
+ printk(KERN_INFO "%s: setting cnfAltRetryCount to %d failed\n",
+ dev->name, local->manual_retry_count);
+ }
+
+ if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1) &&
+ hfa384x_get_rid(dev, HFA384X_RID_CNFDBMADJUST, &tmp, 2, 1) == 2) {
+ local->rssi_to_dBm = le16_to_cpu(tmp);
+ }
+
+ if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->wpa &&
+ hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1)) {
+ printk(KERN_INFO "%s: setting ssnHandlingMode to 1 failed\n",
+ dev->name);
+ }
+
+ if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->generic_elem &&
+ hfa384x_set_rid(dev, HFA384X_RID_GENERICELEMENT,
+ local->generic_elem, local->generic_elem_len)) {
+ printk(KERN_INFO "%s: setting genericElement failed\n",
+ dev->name);
+ }
+
+ fail:
+ return ret;
+}
+
+
+static int prism2_hw_init(struct net_device *dev, int initial)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int ret, first = 1;
+ unsigned long start, delay;
+
+ PDEBUG(DEBUG_FLOW, "prism2_hw_init()\n");
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits);
+
+ init:
+ /* initialize HFA 384x */
+ ret = hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_INIT, 0);
+ if (ret) {
+ printk(KERN_INFO "%s: first command failed - assuming card "
+ "does not have primary firmware\n", dev_info);
+ }
+
+ if (first && (HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) {
+ /* EvStat has Cmd bit set in some cases, so retry once if no
+ * wait was needed */
+ HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+ printk(KERN_DEBUG "%s: init command completed too quickly - "
+ "retrying\n", dev->name);
+ first = 0;
+ goto init;
+ }
+
+ start = jiffies;
+ delay = jiffies + HFA384X_INIT_TIMEOUT;
+ while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) &&
+ time_before(jiffies, delay))
+ yield();
+ if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) {
+ printk(KERN_DEBUG "%s: assuming no Primary image in "
+ "flash - card initialization not completed\n",
+ dev_info);
+ local->no_pri = 1;
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+ if (local->sram_type == -1)
+ local->sram_type = prism2_get_ram_size(local);
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+ return 1;
+ }
+ local->no_pri = 0;
+ printk(KERN_DEBUG "prism2_hw_init: initialized in %lu ms\n",
+ (jiffies - start) * 1000 / HZ);
+ HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+ return 0;
+}
+
+
+static int prism2_hw_init2(struct net_device *dev, int initial)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int i;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+ kfree(local->pda);
+ if (local->no_pri)
+ local->pda = NULL;
+ else
+ local->pda = prism2_read_pda(dev);
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+ hfa384x_disable_interrupts(dev);
+
+#ifndef final_version
+ HFA384X_OUTW(HFA384X_MAGIC, HFA384X_SWSUPPORT0_OFF);
+ if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) {
+ printk("SWSUPPORT0 write/read failed: %04X != %04X\n",
+ HFA384X_INW(HFA384X_SWSUPPORT0_OFF), HFA384X_MAGIC);
+ goto failed;
+ }
+#endif
+
+ if (initial || local->pri_only) {
+ hfa384x_events_only_cmd(dev);
+ /* get card version information */
+ if (prism2_get_version_info(dev, HFA384X_RID_NICID, "NIC") ||
+ prism2_get_version_info(dev, HFA384X_RID_PRIID, "PRI")) {
+ hfa384x_disable_interrupts(dev);
+ goto failed;
+ }
+
+ if (prism2_get_version_info(dev, HFA384X_RID_STAID, "STA")) {
+ printk(KERN_DEBUG "%s: Failed to read STA f/w version "
+ "- only Primary f/w present\n", dev->name);
+ local->pri_only = 1;
+ return 0;
+ }
+ local->pri_only = 0;
+ hfa384x_disable_interrupts(dev);
+ }
+
+ /* FIX: could convert allocate_fid to use sleeping CmdCompl wait and
+ * enable interrupts before this. This would also require some sort of
+ * sleeping AllocEv waiting */
+
+ /* allocate TX FIDs */
+ local->txfid_len = PRISM2_TXFID_LEN;
+ for (i = 0; i < PRISM2_TXFID_COUNT; i++) {
+ local->txfid[i] = hfa384x_allocate_fid(dev, local->txfid_len);
+ if (local->txfid[i] == 0xffff && local->txfid_len > 1600) {
+ local->txfid[i] = hfa384x_allocate_fid(dev, 1600);
+ if (local->txfid[i] != 0xffff) {
+ printk(KERN_DEBUG "%s: Using shorter TX FID "
+ "(1600 bytes)\n", dev->name);
+ local->txfid_len = 1600;
+ }
+ }
+ if (local->txfid[i] == 0xffff)
+ goto failed;
+ local->intransmitfid[i] = PRISM2_TXFID_EMPTY;
+ }
+
+ hfa384x_events_only_cmd(dev);
+
+ if (initial) {
+ struct list_head *ptr;
+ prism2_check_sta_fw_version(local);
+
+ if (hfa384x_get_rid(dev, HFA384X_RID_CNFOWNMACADDR,
+ &dev->dev_addr, 6, 1) < 0) {
+ printk("%s: could not get own MAC address\n",
+ dev->name);
+ }
+ list_for_each(ptr, &local->hostap_interfaces) {
+ iface = list_entry(ptr, struct hostap_interface, list);
+ memcpy(iface->dev->dev_addr, dev->dev_addr, ETH_ALEN);
+ }
+ } else if (local->fw_ap)
+ prism2_check_sta_fw_version(local);
+
+ prism2_setup_rids(dev);
+
+ /* MAC is now configured, but port 0 is not yet enabled */
+ return 0;
+
+ failed:
+ if (!local->no_pri)
+ printk(KERN_WARNING "%s: Initialization failed\n", dev_info);
+ return 1;
+}
+
+
+static int prism2_hw_enable(struct net_device *dev, int initial)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int was_resetting;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ was_resetting = local->hw_resetting;
+
+ if (hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, NULL)) {
+ printk("%s: MAC port 0 enabling failed\n", dev->name);
+ return 1;
+ }
+
+ local->hw_ready = 1;
+ local->hw_reset_tries = 0;
+ local->hw_resetting = 0;
+ hfa384x_enable_interrupts(dev);
+
+ /* at least D-Link DWL-650 seems to require additional port reset
+ * before it starts acting as an AP, so reset port automatically
+ * here just in case */
+ if (initial && prism2_reset_port(dev)) {
+ printk("%s: MAC port 0 reseting failed\n", dev->name);
+ return 1;
+ }
+
+ if (was_resetting && netif_queue_stopped(dev)) {
+ /* If hw_reset() was called during pending transmit, netif
+ * queue was stopped. Wake it up now since the wlan card has
+ * been resetted. */
+ netif_wake_queue(dev);
+ }
+
+ return 0;
+}
+
+
+static int prism2_hw_config(struct net_device *dev, int initial)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->hw_downloading)
+ return 1;
+
+ if (prism2_hw_init(dev, initial)) {
+ return local->no_pri ? 0 : 1;
+ }
+
+ if (prism2_hw_init2(dev, initial))
+ return 1;
+
+ /* Enable firmware if secondary image is loaded and at least one of the
+ * netdevices is up. */
+ if (!local->pri_only &&
+ (initial == 0 || (initial == 2 && local->num_dev_open > 0))) {
+ if (!local->dev_enabled)
+ prism2_callback(local, PRISM2_CALLBACK_ENABLE);
+ local->dev_enabled = 1;
+ return prism2_hw_enable(dev, initial);
+ }
+
+ return 0;
+}
+
+
+static void prism2_hw_shutdown(struct net_device *dev, int no_disable)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ /* Allow only command completion events during disable */
+ hfa384x_events_only_cmd(dev);
+
+ local->hw_ready = 0;
+ if (local->dev_enabled)
+ prism2_callback(local, PRISM2_CALLBACK_DISABLE);
+ local->dev_enabled = 0;
+
+ if (local->func->card_present && !local->func->card_present(local)) {
+ printk(KERN_DEBUG "%s: card already removed or not configured "
+ "during shutdown\n", dev->name);
+ return;
+ }
+
+ if ((no_disable & HOSTAP_HW_NO_DISABLE) == 0 &&
+ hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, NULL))
+ printk(KERN_WARNING "%s: Shutdown failed\n", dev_info);
+
+ hfa384x_disable_interrupts(dev);
+
+ if (no_disable & HOSTAP_HW_ENABLE_CMDCOMPL)
+ hfa384x_events_only_cmd(dev);
+ else
+ prism2_clear_cmd_queue(local);
+}
+
+
+static void prism2_hw_reset(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+#if 0
+ static long last_reset = 0;
+
+ /* do not reset card more than once per second to avoid ending up in a
+ * busy loop reseting the card */
+ if (time_before_eq(jiffies, last_reset + HZ))
+ return;
+ last_reset = jiffies;
+#endif
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (in_interrupt()) {
+ printk(KERN_DEBUG "%s: driver bug - prism2_hw_reset() called "
+ "in interrupt context\n", dev->name);
+ return;
+ }
+
+ if (local->hw_downloading)
+ return;
+
+ if (local->hw_resetting) {
+ printk(KERN_WARNING "%s: %s: already resetting card - "
+ "ignoring reset request\n", dev_info, dev->name);
+ return;
+ }
+
+ local->hw_reset_tries++;
+ if (local->hw_reset_tries > 10) {
+ printk(KERN_WARNING "%s: too many reset tries, skipping\n",
+ dev->name);
+ return;
+ }
+
+ printk(KERN_WARNING "%s: %s: resetting card\n", dev_info, dev->name);
+ hfa384x_disable_interrupts(dev);
+ local->hw_resetting = 1;
+ if (local->func->cor_sreset) {
+ /* Host system seems to hang in some cases with high traffic
+ * load or shared interrupts during COR sreset. Disable shared
+ * interrupts during reset to avoid these crashes. COS sreset
+ * takes quite a long time, so it is unfortunate that this
+ * seems to be needed. Anyway, I do not know of any better way
+ * of avoiding the crash. */
+ disable_irq(dev->irq);
+ local->func->cor_sreset(local);
+ enable_irq(dev->irq);
+ }
+ prism2_hw_shutdown(dev, 1);
+ prism2_hw_config(dev, 0);
+ local->hw_resetting = 0;
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+ if (local->dl_pri) {
+ printk(KERN_DEBUG "%s: persistent download of primary "
+ "firmware\n", dev->name);
+ if (prism2_download_genesis(local, local->dl_pri) < 0)
+ printk(KERN_WARNING "%s: download (PRI) failed\n",
+ dev->name);
+ }
+
+ if (local->dl_sec) {
+ printk(KERN_DEBUG "%s: persistent download of secondary "
+ "firmware\n", dev->name);
+ if (prism2_download_volatile(local, local->dl_sec) < 0)
+ printk(KERN_WARNING "%s: download (SEC) failed\n",
+ dev->name);
+ }
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+ /* TODO: restore beacon TIM bits for STAs that have buffered frames */
+}
+
+
+static void prism2_schedule_reset(local_info_t *local)
+{
+ schedule_work(&local->reset_queue);
+}
+
+
+/* Called only as scheduled task after noticing card timeout in interrupt
+ * context */
+static void handle_reset_queue(void *data)
+{
+ local_info_t *local = (local_info_t *) data;
+
+ printk(KERN_DEBUG "%s: scheduled card reset\n", local->dev->name);
+ prism2_hw_reset(local->dev);
+
+ if (netif_queue_stopped(local->dev)) {
+ int i;
+
+ for (i = 0; i < PRISM2_TXFID_COUNT; i++)
+ if (local->intransmitfid[i] == PRISM2_TXFID_EMPTY) {
+ PDEBUG(DEBUG_EXTRA, "prism2_tx_timeout: "
+ "wake up queue\n");
+ netif_wake_queue(local->dev);
+ break;
+ }
+ }
+}
+
+
+static int prism2_get_txfid_idx(local_info_t *local)
+{
+ int idx, end;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->txfidlock, flags);
+ end = idx = local->next_txfid;
+ do {
+ if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) {
+ local->intransmitfid[idx] = PRISM2_TXFID_RESERVED;
+ spin_unlock_irqrestore(&local->txfidlock, flags);
+ return idx;
+ }
+ idx++;
+ if (idx >= PRISM2_TXFID_COUNT)
+ idx = 0;
+ } while (idx != end);
+ spin_unlock_irqrestore(&local->txfidlock, flags);
+
+ PDEBUG(DEBUG_EXTRA2, "prism2_get_txfid_idx: no room in txfid buf: "
+ "packet dropped\n");
+ local->stats.tx_dropped++;
+
+ return -1;
+}
+
+
+/* Called only from hardware IRQ */
+static void prism2_transmit_cb(struct net_device *dev, long context,
+ u16 resp0, u16 res)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int idx = (int) context;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (res) {
+ printk(KERN_DEBUG "%s: prism2_transmit_cb - res=0x%02x\n",
+ dev->name, res);
+ return;
+ }
+
+ if (idx < 0 || idx >= PRISM2_TXFID_COUNT) {
+ printk(KERN_DEBUG "%s: prism2_transmit_cb called with invalid "
+ "idx=%d\n", dev->name, idx);
+ return;
+ }
+
+ if (!test_and_clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) {
+ printk(KERN_DEBUG "%s: driver bug: prism2_transmit_cb called "
+ "with no pending transmit\n", dev->name);
+ }
+
+ if (netif_queue_stopped(dev)) {
+ /* ready for next TX, so wake up queue that was stopped in
+ * prism2_transmit() */
+ netif_wake_queue(dev);
+ }
+
+ spin_lock(&local->txfidlock);
+
+ /* With reclaim, Resp0 contains new txfid for transmit; the old txfid
+ * will be automatically allocated for the next TX frame */
+ local->intransmitfid[idx] = resp0;
+
+ PDEBUG(DEBUG_FID, "%s: prism2_transmit_cb: txfid[%d]=0x%04x, "
+ "resp0=0x%04x, transmit_txfid=0x%04x\n",
+ dev->name, idx, local->txfid[idx],
+ resp0, local->intransmitfid[local->next_txfid]);
+
+ idx++;
+ if (idx >= PRISM2_TXFID_COUNT)
+ idx = 0;
+ local->next_txfid = idx;
+
+ /* check if all TX buffers are occupied */
+ do {
+ if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) {
+ spin_unlock(&local->txfidlock);
+ return;
+ }
+ idx++;
+ if (idx >= PRISM2_TXFID_COUNT)
+ idx = 0;
+ } while (idx != local->next_txfid);
+ spin_unlock(&local->txfidlock);
+
+ /* no empty TX buffers, stop queue */
+ netif_stop_queue(dev);
+}
+
+
+/* Called only from software IRQ if PCI bus master is not used (with bus master
+ * this can be called both from software and hardware IRQ) */
+static int prism2_transmit(struct net_device *dev, int idx)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int res;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ /* The driver tries to stop netif queue so that there would not be
+ * more than one attempt to transmit frames going on; check that this
+ * is really the case */
+
+ if (test_and_set_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) {
+ printk(KERN_DEBUG "%s: driver bug - prism2_transmit() called "
+ "when previous TX was pending\n", dev->name);
+ return -1;
+ }
+
+ /* stop the queue for the time that transmit is pending */
+ netif_stop_queue(dev);
+
+ /* transmit packet */
+ res = hfa384x_cmd_callback(
+ dev,
+ HFA384X_CMDCODE_TRANSMIT | HFA384X_CMD_TX_RECLAIM,
+ local->txfid[idx],
+ prism2_transmit_cb, (long) idx);
+
+ if (res) {
+ struct net_device_stats *stats;
+ printk(KERN_DEBUG "%s: prism2_transmit: CMDCODE_TRANSMIT "
+ "failed (res=%d)\n", dev->name, res);
+ stats = hostap_get_stats(dev);
+ stats->tx_dropped++;
+ netif_wake_queue(dev);
+ return -1;
+ }
+ dev->trans_start = jiffies;
+
+ /* Since we did not wait for command completion, the card continues
+ * to process on the background and we will finish handling when
+ * command completion event is handled (prism2_cmd_ev() function) */
+
+ return 0;
+}
+
+
+/* Send IEEE 802.11 frame (convert the header into Prism2 TX descriptor and
+ * send the payload with this descriptor) */
+/* Called only from software IRQ */
+static int prism2_tx_80211(struct sk_buff *skb, struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct hfa384x_tx_frame txdesc;
+ struct hostap_skb_tx_data *meta;
+ int hdr_len, data_len, idx, res, ret = -1;
+ u16 tx_control, fc;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ meta = (struct hostap_skb_tx_data *) skb->cb;
+
+ prism2_callback(local, PRISM2_CALLBACK_TX_START);
+
+ if ((local->func->card_present && !local->func->card_present(local)) ||
+ !local->hw_ready || local->hw_downloading || local->pri_only) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: prism2_tx_80211: hw not ready -"
+ " skipping\n", dev->name);
+ }
+ goto fail;
+ }
+
+ memset(&txdesc, 0, sizeof(txdesc));
+
+ /* skb->data starts with txdesc->frame_control */
+ hdr_len = 24;
+ memcpy(&txdesc.frame_control, skb->data, hdr_len);
+ fc = le16_to_cpu(txdesc.frame_control);
+ if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
+ (fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS) &&
+ skb->len >= 30) {
+ /* Addr4 */
+ memcpy(txdesc.addr4, skb->data + hdr_len, ETH_ALEN);
+ hdr_len += ETH_ALEN;
+ }
+
+ tx_control = local->tx_control;
+ if (meta->tx_cb_idx) {
+ tx_control |= HFA384X_TX_CTRL_TX_OK;
+ txdesc.sw_support = cpu_to_le16(meta->tx_cb_idx);
+ }
+ txdesc.tx_control = cpu_to_le16(tx_control);
+ txdesc.tx_rate = meta->rate;
+
+ data_len = skb->len - hdr_len;
+ txdesc.data_len = cpu_to_le16(data_len);
+ txdesc.len = cpu_to_be16(data_len);
+
+ idx = prism2_get_txfid_idx(local);
+ if (idx < 0)
+ goto fail;
+
+ if (local->frame_dump & PRISM2_DUMP_TX_HDR)
+ hostap_dump_tx_header(dev->name, &txdesc);
+
+ spin_lock(&local->baplock);
+ res = hfa384x_setup_bap(dev, BAP0, local->txfid[idx], 0);
+
+ if (!res)
+ res = hfa384x_to_bap(dev, BAP0, &txdesc, sizeof(txdesc));
+ if (!res)
+ res = hfa384x_to_bap(dev, BAP0, skb->data + hdr_len,
+ skb->len - hdr_len);
+ spin_unlock(&local->baplock);
+
+ if (!res)
+ res = prism2_transmit(dev, idx);
+ if (res) {
+ printk(KERN_DEBUG "%s: prism2_tx_80211 - to BAP0 failed\n",
+ dev->name);
+ local->intransmitfid[idx] = PRISM2_TXFID_EMPTY;
+ schedule_work(&local->reset_queue);
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+ prism2_callback(local, PRISM2_CALLBACK_TX_END);
+ return ret;
+}
+
+
+/* Some SMP systems have reported number of odd errors with hostap_pci. fid
+ * register has changed values between consecutive reads for an unknown reason.
+ * This should really not happen, so more debugging is needed. This test
+ * version is a big slower, but it will detect most of such register changes
+ * and will try to get the correct fid eventually. */
+#define EXTRA_FID_READ_TESTS
+
+static inline u16 prism2_read_fid_reg(struct net_device *dev, u16 reg)
+{
+#ifdef EXTRA_FID_READ_TESTS
+ u16 val, val2, val3;
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ val = HFA384X_INW(reg);
+ val2 = HFA384X_INW(reg);
+ val3 = HFA384X_INW(reg);
+
+ if (val == val2 && val == val3)
+ return val;
+
+ printk(KERN_DEBUG "%s: detected fid change (try=%d, reg=%04x):"
+ " %04x %04x %04x\n",
+ dev->name, i, reg, val, val2, val3);
+ if ((val == val2 || val == val3) && val != 0)
+ return val;
+ if (val2 == val3 && val2 != 0)
+ return val2;
+ }
+ printk(KERN_WARNING "%s: Uhhuh.. could not read good fid from reg "
+ "%04x (%04x %04x %04x)\n", dev->name, reg, val, val2, val3);
+ return val;
+#else /* EXTRA_FID_READ_TESTS */
+ return HFA384X_INW(reg);
+#endif /* EXTRA_FID_READ_TESTS */
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_rx(local_info_t *local)
+{
+ struct net_device *dev = local->dev;
+ int res, rx_pending = 0;
+ u16 len, hdr_len, rxfid, status, macport;
+ struct net_device_stats *stats;
+ struct hfa384x_rx_frame rxdesc;
+ struct sk_buff *skb = NULL;
+
+ prism2_callback(local, PRISM2_CALLBACK_RX_START);
+ stats = hostap_get_stats(dev);
+
+ rxfid = prism2_read_fid_reg(dev, HFA384X_RXFID_OFF);
+#ifndef final_version
+ if (rxfid == 0) {
+ rxfid = HFA384X_INW(HFA384X_RXFID_OFF);
+ printk(KERN_DEBUG "prism2_rx: rxfid=0 (next 0x%04x)\n",
+ rxfid);
+ if (rxfid == 0) {
+ schedule_work(&local->reset_queue);
+ goto rx_dropped;
+ }
+ /* try to continue with the new rxfid value */
+ }
+#endif
+
+ spin_lock(&local->baplock);
+ res = hfa384x_setup_bap(dev, BAP0, rxfid, 0);
+ if (!res)
+ res = hfa384x_from_bap(dev, BAP0, &rxdesc, sizeof(rxdesc));
+
+ if (res) {
+ spin_unlock(&local->baplock);
+ printk(KERN_DEBUG "%s: copy from BAP0 failed %d\n", dev->name,
+ res);
+ if (res == -ETIMEDOUT) {
+ schedule_work(&local->reset_queue);
+ }
+ goto rx_dropped;
+ }
+
+ len = le16_to_cpu(rxdesc.data_len);
+ hdr_len = sizeof(rxdesc);
+ status = le16_to_cpu(rxdesc.status);
+ macport = (status >> 8) & 0x07;
+
+ /* Drop frames with too large reported payload length. Monitor mode
+ * seems to sometimes pass frames (e.g., ctrl::ack) with signed and
+ * negative value, so allow also values 65522 .. 65534 (-14 .. -2) for
+ * macport 7 */
+ if (len > PRISM2_DATA_MAXLEN + 8 /* WEP */) {
+ if (macport == 7 && local->iw_mode == IW_MODE_MONITOR) {
+ if (len >= (u16) -14) {
+ hdr_len -= 65535 - len;
+ hdr_len--;
+ }
+ len = 0;
+ } else {
+ spin_unlock(&local->baplock);
+ printk(KERN_DEBUG "%s: Received frame with invalid "
+ "length 0x%04x\n", dev->name, len);
+ hostap_dump_rx_header(dev->name, &rxdesc);
+ goto rx_dropped;
+ }
+ }
+
+ skb = dev_alloc_skb(len + hdr_len);
+ if (!skb) {
+ spin_unlock(&local->baplock);
+ printk(KERN_DEBUG "%s: RX failed to allocate skb\n",
+ dev->name);
+ goto rx_dropped;
+ }
+ skb->dev = dev;
+ memcpy(skb_put(skb, hdr_len), &rxdesc, hdr_len);
+
+ if (len > 0)
+ res = hfa384x_from_bap(dev, BAP0, skb_put(skb, len), len);
+ spin_unlock(&local->baplock);
+ if (res) {
+ printk(KERN_DEBUG "%s: RX failed to read "
+ "frame data\n", dev->name);
+ goto rx_dropped;
+ }
+
+ skb_queue_tail(&local->rx_list, skb);
+ tasklet_schedule(&local->rx_tasklet);
+
+ rx_exit:
+ prism2_callback(local, PRISM2_CALLBACK_RX_END);
+ if (!rx_pending) {
+ HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF);
+ }
+
+ return;
+
+ rx_dropped:
+ stats->rx_dropped++;
+ if (skb)
+ dev_kfree_skb(skb);
+ goto rx_exit;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_rx_skb(local_info_t *local, struct sk_buff *skb)
+{
+ struct hfa384x_rx_frame *rxdesc;
+ struct net_device *dev = skb->dev;
+ struct hostap_80211_rx_status stats;
+ int hdrlen, rx_hdrlen;
+
+ rx_hdrlen = sizeof(*rxdesc);
+ if (skb->len < sizeof(*rxdesc)) {
+ /* Allow monitor mode to receive shorter frames */
+ if (local->iw_mode == IW_MODE_MONITOR &&
+ skb->len >= sizeof(*rxdesc) - 30) {
+ rx_hdrlen = skb->len;
+ } else {
+ dev_kfree_skb(skb);
+ return;
+ }
+ }
+
+ rxdesc = (struct hfa384x_rx_frame *) skb->data;
+
+ if (local->frame_dump & PRISM2_DUMP_RX_HDR &&
+ skb->len >= sizeof(*rxdesc))
+ hostap_dump_rx_header(dev->name, rxdesc);
+
+ if (le16_to_cpu(rxdesc->status) & HFA384X_RX_STATUS_FCSERR &&
+ (!local->monitor_allow_fcserr ||
+ local->iw_mode != IW_MODE_MONITOR))
+ goto drop;
+
+ if (skb->len > PRISM2_DATA_MAXLEN) {
+ printk(KERN_DEBUG "%s: RX: len(%d) > MAX(%d)\n",
+ dev->name, skb->len, PRISM2_DATA_MAXLEN);
+ goto drop;
+ }
+
+ stats.mac_time = le32_to_cpu(rxdesc->time);
+ stats.signal = rxdesc->signal - local->rssi_to_dBm;
+ stats.noise = rxdesc->silence - local->rssi_to_dBm;
+ stats.rate = rxdesc->rate;
+
+ /* Convert Prism2 RX structure into IEEE 802.11 header */
+ hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(rxdesc->frame_control));
+ if (hdrlen > rx_hdrlen)
+ hdrlen = rx_hdrlen;
+
+ memmove(skb_pull(skb, rx_hdrlen - hdrlen),
+ &rxdesc->frame_control, hdrlen);
+
+ hostap_80211_rx(dev, skb, &stats);
+ return;
+
+ drop:
+ dev_kfree_skb(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_rx_tasklet(unsigned long data)
+{
+ local_info_t *local = (local_info_t *) data;
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&local->rx_list)) != NULL)
+ hostap_rx_skb(local, skb);
+}
+
+
+/* Called only from hardware IRQ */
+static void prism2_alloc_ev(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int idx;
+ u16 fid;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ fid = prism2_read_fid_reg(dev, HFA384X_ALLOCFID_OFF);
+
+ PDEBUG(DEBUG_FID, "FID: interrupt: ALLOC - fid=0x%04x\n", fid);
+
+ spin_lock(&local->txfidlock);
+ idx = local->next_alloc;
+
+ do {
+ if (local->txfid[idx] == fid) {
+ PDEBUG(DEBUG_FID, "FID: found matching txfid[%d]\n",
+ idx);
+
+#ifndef final_version
+ if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY)
+ printk("Already released txfid found at idx "
+ "%d\n", idx);
+ if (local->intransmitfid[idx] == PRISM2_TXFID_RESERVED)
+ printk("Already reserved txfid found at idx "
+ "%d\n", idx);
+#endif
+ local->intransmitfid[idx] = PRISM2_TXFID_EMPTY;
+ idx++;
+ local->next_alloc = idx >= PRISM2_TXFID_COUNT ? 0 :
+ idx;
+
+ if (!test_bit(HOSTAP_BITS_TRANSMIT, &local->bits) &&
+ netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+
+ spin_unlock(&local->txfidlock);
+ return;
+ }
+
+ idx++;
+ if (idx >= PRISM2_TXFID_COUNT)
+ idx = 0;
+ } while (idx != local->next_alloc);
+
+ printk(KERN_WARNING "%s: could not find matching txfid (0x%04x, new "
+ "read 0x%04x) for alloc event\n", dev->name, fid,
+ HFA384X_INW(HFA384X_ALLOCFID_OFF));
+ printk(KERN_DEBUG "TXFIDs:");
+ for (idx = 0; idx < PRISM2_TXFID_COUNT; idx++)
+ printk(" %04x[%04x]", local->txfid[idx],
+ local->intransmitfid[idx]);
+ printk("\n");
+ spin_unlock(&local->txfidlock);
+
+ /* FIX: should probably schedule reset; reference to one txfid was lost
+ * completely.. Bad things will happen if we run out of txfids
+ * Actually, this will cause netdev watchdog to notice TX timeout and
+ * then card reset after all txfids have been leaked. */
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_tx_callback(local_info_t *local,
+ struct hfa384x_tx_frame *txdesc, int ok,
+ char *payload)
+{
+ u16 sw_support, hdrlen, len;
+ struct sk_buff *skb;
+ struct hostap_tx_callback_info *cb;
+
+ /* Make sure that frame was from us. */
+ if (memcmp(txdesc->addr2, local->dev->dev_addr, ETH_ALEN)) {
+ printk(KERN_DEBUG "%s: TX callback - foreign frame\n",
+ local->dev->name);
+ return;
+ }
+
+ sw_support = le16_to_cpu(txdesc->sw_support);
+
+ spin_lock(&local->lock);
+ cb = local->tx_callback;
+ while (cb != NULL && cb->idx != sw_support)
+ cb = cb->next;
+ spin_unlock(&local->lock);
+
+ if (cb == NULL) {
+ printk(KERN_DEBUG "%s: could not find TX callback (idx %d)\n",
+ local->dev->name, sw_support);
+ return;
+ }
+
+ hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(txdesc->frame_control));
+ len = le16_to_cpu(txdesc->data_len);
+ skb = dev_alloc_skb(hdrlen + len);
+ if (skb == NULL) {
+ printk(KERN_DEBUG "%s: hostap_tx_callback failed to allocate "
+ "skb\n", local->dev->name);
+ return;
+ }
+
+ memcpy(skb_put(skb, hdrlen), (void *) &txdesc->frame_control, hdrlen);
+ if (payload)
+ memcpy(skb_put(skb, len), payload, len);
+
+ skb->dev = local->dev;
+ skb->mac.raw = skb->data;
+
+ cb->func(skb, ok, cb->data);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static int hostap_tx_compl_read(local_info_t *local, int error,
+ struct hfa384x_tx_frame *txdesc,
+ char **payload)
+{
+ u16 fid, len;
+ int res, ret = 0;
+ struct net_device *dev = local->dev;
+
+ fid = prism2_read_fid_reg(dev, HFA384X_TXCOMPLFID_OFF);
+
+ PDEBUG(DEBUG_FID, "interrupt: TX (err=%d) - fid=0x%04x\n", fid, error);
+
+ spin_lock(&local->baplock);
+ res = hfa384x_setup_bap(dev, BAP0, fid, 0);
+ if (!res)
+ res = hfa384x_from_bap(dev, BAP0, txdesc, sizeof(*txdesc));
+ if (res) {
+ PDEBUG(DEBUG_EXTRA, "%s: TX (err=%d) - fid=0x%04x - could not "
+ "read txdesc\n", dev->name, error, fid);
+ if (res == -ETIMEDOUT) {
+ schedule_work(&local->reset_queue);
+ }
+ ret = -1;
+ goto fail;
+ }
+ if (txdesc->sw_support) {
+ len = le16_to_cpu(txdesc->data_len);
+ if (len < PRISM2_DATA_MAXLEN) {
+ *payload = (char *) kmalloc(len, GFP_ATOMIC);
+ if (*payload == NULL ||
+ hfa384x_from_bap(dev, BAP0, *payload, len)) {
+ PDEBUG(DEBUG_EXTRA, "%s: could not read TX "
+ "frame payload\n", dev->name);
+ kfree(*payload);
+ *payload = NULL;
+ ret = -1;
+ goto fail;
+ }
+ }
+ }
+
+ fail:
+ spin_unlock(&local->baplock);
+
+ return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_tx_ev(local_info_t *local)
+{
+ struct net_device *dev = local->dev;
+ char *payload = NULL;
+ struct hfa384x_tx_frame txdesc;
+
+ if (hostap_tx_compl_read(local, 0, &txdesc, &payload))
+ goto fail;
+
+ if (local->frame_dump & PRISM2_DUMP_TX_HDR) {
+ PDEBUG(DEBUG_EXTRA, "%s: TX - status=0x%04x "
+ "retry_count=%d tx_rate=%d seq_ctrl=%d "
+ "duration_id=%d\n",
+ dev->name, le16_to_cpu(txdesc.status),
+ txdesc.retry_count, txdesc.tx_rate,
+ le16_to_cpu(txdesc.seq_ctrl),
+ le16_to_cpu(txdesc.duration_id));
+ }
+
+ if (txdesc.sw_support)
+ hostap_tx_callback(local, &txdesc, 1, payload);
+ kfree(payload);
+
+ fail:
+ HFA384X_OUTW(HFA384X_EV_TX, HFA384X_EVACK_OFF);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_sta_tx_exc_tasklet(unsigned long data)
+{
+ local_info_t *local = (local_info_t *) data;
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&local->sta_tx_exc_list)) != NULL) {
+ struct hfa384x_tx_frame *txdesc =
+ (struct hfa384x_tx_frame *) skb->data;
+
+ if (skb->len >= sizeof(*txdesc)) {
+ /* Convert Prism2 RX structure into IEEE 802.11 header
+ */
+ u16 fc = le16_to_cpu(txdesc->frame_control);
+ int hdrlen = hostap_80211_get_hdrlen(fc);
+ memmove(skb_pull(skb, sizeof(*txdesc) - hdrlen),
+ &txdesc->frame_control, hdrlen);
+
+ hostap_handle_sta_tx_exc(local, skb);
+ }
+ dev_kfree_skb(skb);
+ }
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_txexc(local_info_t *local)
+{
+ struct net_device *dev = local->dev;
+ u16 status, fc;
+ int show_dump, res;
+ char *payload = NULL;
+ struct hfa384x_tx_frame txdesc;
+
+ show_dump = local->frame_dump & PRISM2_DUMP_TXEXC_HDR;
+ local->stats.tx_errors++;
+
+ res = hostap_tx_compl_read(local, 1, &txdesc, &payload);
+ HFA384X_OUTW(HFA384X_EV_TXEXC, HFA384X_EVACK_OFF);
+ if (res)
+ return;
+
+ status = le16_to_cpu(txdesc.status);
+
+ /* We produce a TXDROP event only for retry or lifetime
+ * exceeded, because that's the only status that really mean
+ * that this particular node went away.
+ * Other errors means that *we* screwed up. - Jean II */
+ if (status & (HFA384X_TX_STATUS_RETRYERR | HFA384X_TX_STATUS_AGEDERR))
+ {
+ union iwreq_data wrqu;
+
+ /* Copy 802.11 dest address. */
+ memcpy(wrqu.addr.sa_data, txdesc.addr1, ETH_ALEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
+ } else
+ show_dump = 1;
+
+ if (local->iw_mode == IW_MODE_MASTER ||
+ local->iw_mode == IW_MODE_REPEAT ||
+ local->wds_type & HOSTAP_WDS_AP_CLIENT) {
+ struct sk_buff *skb;
+ skb = dev_alloc_skb(sizeof(txdesc));
+ if (skb) {
+ memcpy(skb_put(skb, sizeof(txdesc)), &txdesc,
+ sizeof(txdesc));
+ skb_queue_tail(&local->sta_tx_exc_list, skb);
+ tasklet_schedule(&local->sta_tx_exc_tasklet);
+ }
+ }
+
+ if (txdesc.sw_support)
+ hostap_tx_callback(local, &txdesc, 0, payload);
+ kfree(payload);
+
+ if (!show_dump)
+ return;
+
+ PDEBUG(DEBUG_EXTRA, "%s: TXEXC - status=0x%04x (%s%s%s%s)"
+ " tx_control=%04x\n",
+ dev->name, status,
+ status & HFA384X_TX_STATUS_RETRYERR ? "[RetryErr]" : "",
+ status & HFA384X_TX_STATUS_AGEDERR ? "[AgedErr]" : "",
+ status & HFA384X_TX_STATUS_DISCON ? "[Discon]" : "",
+ status & HFA384X_TX_STATUS_FORMERR ? "[FormErr]" : "",
+ le16_to_cpu(txdesc.tx_control));
+
+ fc = le16_to_cpu(txdesc.frame_control);
+ PDEBUG(DEBUG_EXTRA, " retry_count=%d tx_rate=%d fc=0x%04x "
+ "(%s%s%s::%d%s%s)\n",
+ txdesc.retry_count, txdesc.tx_rate, fc,
+ WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT ? "Mgmt" : "",
+ WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_CTL ? "Ctrl" : "",
+ WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA ? "Data" : "",
+ WLAN_FC_GET_STYPE(fc) >> 4,
+ fc & IEEE80211_FCTL_TODS ? " ToDS" : "",
+ fc & IEEE80211_FCTL_FROMDS ? " FromDS" : "");
+ PDEBUG(DEBUG_EXTRA, " A1=" MACSTR " A2=" MACSTR " A3="
+ MACSTR " A4=" MACSTR "\n",
+ MAC2STR(txdesc.addr1), MAC2STR(txdesc.addr2),
+ MAC2STR(txdesc.addr3), MAC2STR(txdesc.addr4));
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_info_tasklet(unsigned long data)
+{
+ local_info_t *local = (local_info_t *) data;
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&local->info_list)) != NULL) {
+ hostap_info_process(local, skb);
+ dev_kfree_skb(skb);
+ }
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_info(local_info_t *local)
+{
+ struct net_device *dev = local->dev;
+ u16 fid;
+ int res, left;
+ struct hfa384x_info_frame info;
+ struct sk_buff *skb;
+
+ fid = HFA384X_INW(HFA384X_INFOFID_OFF);
+
+ spin_lock(&local->baplock);
+ res = hfa384x_setup_bap(dev, BAP0, fid, 0);
+ if (!res)
+ res = hfa384x_from_bap(dev, BAP0, &info, sizeof(info));
+ if (res) {
+ spin_unlock(&local->baplock);
+ printk(KERN_DEBUG "Could not get info frame (fid=0x%04x)\n",
+ fid);
+ if (res == -ETIMEDOUT) {
+ schedule_work(&local->reset_queue);
+ }
+ goto out;
+ }
+
+ le16_to_cpus(&info.len);
+ le16_to_cpus(&info.type);
+ left = (info.len - 1) * 2;
+
+ if (info.len & 0x8000 || info.len == 0 || left > 2060) {
+ /* data register seems to give 0x8000 in some error cases even
+ * though busy bit is not set in offset register;
+ * in addition, length must be at least 1 due to type field */
+ spin_unlock(&local->baplock);
+ printk(KERN_DEBUG "%s: Received info frame with invalid "
+ "length 0x%04x (type 0x%04x)\n", dev->name, info.len,
+ info.type);
+ goto out;
+ }
+
+ skb = dev_alloc_skb(sizeof(info) + left);
+ if (skb == NULL) {
+ spin_unlock(&local->baplock);
+ printk(KERN_DEBUG "%s: Could not allocate skb for info "
+ "frame\n", dev->name);
+ goto out;
+ }
+
+ memcpy(skb_put(skb, sizeof(info)), &info, sizeof(info));
+ if (left > 0 && hfa384x_from_bap(dev, BAP0, skb_put(skb, left), left))
+ {
+ spin_unlock(&local->baplock);
+ printk(KERN_WARNING "%s: Info frame read failed (fid=0x%04x, "
+ "len=0x%04x, type=0x%04x\n",
+ dev->name, fid, info.len, info.type);
+ dev_kfree_skb(skb);
+ goto out;
+ }
+ spin_unlock(&local->baplock);
+
+ skb_queue_tail(&local->info_list, skb);
+ tasklet_schedule(&local->info_tasklet);
+
+ out:
+ HFA384X_OUTW(HFA384X_EV_INFO, HFA384X_EVACK_OFF);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_bap_tasklet(unsigned long data)
+{
+ local_info_t *local = (local_info_t *) data;
+ struct net_device *dev = local->dev;
+ u16 ev;
+ int frames = 30;
+
+ if (local->func->card_present && !local->func->card_present(local))
+ return;
+
+ set_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits);
+
+ /* Process all pending BAP events without generating new interrupts
+ * for them */
+ while (frames-- > 0) {
+ ev = HFA384X_INW(HFA384X_EVSTAT_OFF);
+ if (ev == 0xffff || !(ev & HFA384X_BAP0_EVENTS))
+ break;
+ if (ev & HFA384X_EV_RX)
+ prism2_rx(local);
+ if (ev & HFA384X_EV_INFO)
+ prism2_info(local);
+ if (ev & HFA384X_EV_TX)
+ prism2_tx_ev(local);
+ if (ev & HFA384X_EV_TXEXC)
+ prism2_txexc(local);
+ }
+
+ set_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits);
+ clear_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits);
+
+ /* Enable interrupts for new BAP events */
+ hfa384x_events_all(dev);
+ clear_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits);
+}
+
+
+/* Called only from hardware IRQ */
+static void prism2_infdrop(struct net_device *dev)
+{
+ static unsigned long last_inquire = 0;
+
+ PDEBUG(DEBUG_EXTRA, "%s: INFDROP event\n", dev->name);
+
+ /* some firmware versions seem to get stuck with
+ * full CommTallies in high traffic load cases; every
+ * packet will then cause INFDROP event and CommTallies
+ * info frame will not be sent automatically. Try to
+ * get out of this state by inquiring CommTallies. */
+ if (!last_inquire || time_after(jiffies, last_inquire + HZ)) {
+ hfa384x_cmd_callback(dev, HFA384X_CMDCODE_INQUIRE,
+ HFA384X_INFO_COMMTALLIES, NULL, 0);
+ last_inquire = jiffies;
+ }
+}
+
+
+/* Called only from hardware IRQ */
+static void prism2_ev_tick(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 evstat, inten;
+ static int prev_stuck = 0;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (time_after(jiffies, local->last_tick_timer + 5 * HZ) &&
+ local->last_tick_timer) {
+ evstat = HFA384X_INW(HFA384X_EVSTAT_OFF);
+ inten = HFA384X_INW(HFA384X_INTEN_OFF);
+ if (!prev_stuck) {
+ printk(KERN_INFO "%s: SW TICK stuck? "
+ "bits=0x%lx EvStat=%04x IntEn=%04x\n",
+ dev->name, local->bits, evstat, inten);
+ }
+ local->sw_tick_stuck++;
+ if ((evstat & HFA384X_BAP0_EVENTS) &&
+ (inten & HFA384X_BAP0_EVENTS)) {
+ printk(KERN_INFO "%s: trying to recover from IRQ "
+ "hang\n", dev->name);
+ hfa384x_events_no_bap0(dev);
+ }
+ prev_stuck = 1;
+ } else
+ prev_stuck = 0;
+}
+
+
+/* Called only from hardware IRQ */
+static inline void prism2_check_magic(local_info_t *local)
+{
+ /* at least PCI Prism2.5 with bus mastering seems to sometimes
+ * return 0x0000 in SWSUPPORT0 for unknown reason, but re-reading the
+ * register once or twice seems to get the correct value.. PCI cards
+ * cannot anyway be removed during normal operation, so there is not
+ * really any need for this verification with them. */
+
+#ifndef PRISM2_PCI
+#ifndef final_version
+ static unsigned long last_magic_err = 0;
+ struct net_device *dev = local->dev;
+
+ if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) {
+ if (!local->hw_ready)
+ return;
+ HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
+ if (time_after(jiffies, last_magic_err + 10 * HZ)) {
+ printk("%s: Interrupt, but SWSUPPORT0 does not match: "
+ "%04X != %04X - card removed?\n", dev->name,
+ HFA384X_INW(HFA384X_SWSUPPORT0_OFF),
+ HFA384X_MAGIC);
+ last_magic_err = jiffies;
+ } else if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: interrupt - SWSUPPORT0=%04x "
+ "MAGIC=%04x\n", dev->name,
+ HFA384X_INW(HFA384X_SWSUPPORT0_OFF),
+ HFA384X_MAGIC);
+ }
+ if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != 0xffff)
+ schedule_work(&local->reset_queue);
+ return;
+ }
+#endif /* final_version */
+#endif /* !PRISM2_PCI */
+}
+
+
+/* Called only from hardware IRQ */
+static irqreturn_t prism2_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int events = 0;
+ u16 ev;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0);
+
+ if (local->func->card_present && !local->func->card_present(local)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: Interrupt, but dev not OK\n",
+ dev->name);
+ }
+ return IRQ_HANDLED;
+ }
+
+ prism2_check_magic(local);
+
+ for (;;) {
+ ev = HFA384X_INW(HFA384X_EVSTAT_OFF);
+ if (ev == 0xffff) {
+ if (local->shutdown)
+ return IRQ_HANDLED;
+ HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
+ printk(KERN_DEBUG "%s: prism2_interrupt: ev=0xffff\n",
+ dev->name);
+ return IRQ_HANDLED;
+ }
+
+ ev &= HFA384X_INW(HFA384X_INTEN_OFF);
+ if (ev == 0)
+ break;
+
+ if (ev & HFA384X_EV_CMD) {
+ prism2_cmd_ev(dev);
+ }
+
+ /* Above events are needed even before hw is ready, but other
+ * events should be skipped during initialization. This may
+ * change for AllocEv if allocate_fid is implemented without
+ * busy waiting. */
+ if (!local->hw_ready || local->hw_resetting ||
+ !local->dev_enabled) {
+ ev = HFA384X_INW(HFA384X_EVSTAT_OFF);
+ if (ev & HFA384X_EV_CMD)
+ goto next_event;
+ if ((ev & HFA384X_EVENT_MASK) == 0)
+ return IRQ_HANDLED;
+ if (local->dev_enabled && (ev & ~HFA384X_EV_TICK) &&
+ net_ratelimit()) {
+ printk(KERN_DEBUG "%s: prism2_interrupt: hw "
+ "not ready; skipping events 0x%04x "
+ "(IntEn=0x%04x)%s%s%s\n",
+ dev->name, ev,
+ HFA384X_INW(HFA384X_INTEN_OFF),
+ !local->hw_ready ? " (!hw_ready)" : "",
+ local->hw_resetting ?
+ " (hw_resetting)" : "",
+ !local->dev_enabled ?
+ " (!dev_enabled)" : "");
+ }
+ HFA384X_OUTW(ev, HFA384X_EVACK_OFF);
+ return IRQ_HANDLED;
+ }
+
+ if (ev & HFA384X_EV_TICK) {
+ prism2_ev_tick(dev);
+ HFA384X_OUTW(HFA384X_EV_TICK, HFA384X_EVACK_OFF);
+ }
+
+ if (ev & HFA384X_EV_ALLOC) {
+ prism2_alloc_ev(dev);
+ HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF);
+ }
+
+ /* Reading data from the card is quite time consuming, so do it
+ * in tasklets. TX, TXEXC, RX, and INFO events will be ACKed
+ * and unmasked after needed data has been read completely. */
+ if (ev & HFA384X_BAP0_EVENTS) {
+ hfa384x_events_no_bap0(dev);
+ tasklet_schedule(&local->bap_tasklet);
+ }
+
+#ifndef final_version
+ if (ev & HFA384X_EV_WTERR) {
+ PDEBUG(DEBUG_EXTRA, "%s: WTERR event\n", dev->name);
+ HFA384X_OUTW(HFA384X_EV_WTERR, HFA384X_EVACK_OFF);
+ }
+#endif /* final_version */
+
+ if (ev & HFA384X_EV_INFDROP) {
+ prism2_infdrop(dev);
+ HFA384X_OUTW(HFA384X_EV_INFDROP, HFA384X_EVACK_OFF);
+ }
+
+ next_event:
+ events++;
+ if (events >= PRISM2_MAX_INTERRUPT_EVENTS) {
+ PDEBUG(DEBUG_EXTRA, "prism2_interrupt: >%d events "
+ "(EvStat=0x%04x)\n",
+ PRISM2_MAX_INTERRUPT_EVENTS,
+ HFA384X_INW(HFA384X_EVSTAT_OFF));
+ break;
+ }
+ }
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 1);
+ return IRQ_RETVAL(events);
+}
+
+
+static void prism2_check_sta_fw_version(local_info_t *local)
+{
+ struct hfa384x_comp_ident comp;
+ int id, variant, major, minor;
+
+ if (hfa384x_get_rid(local->dev, HFA384X_RID_STAID,
+ &comp, sizeof(comp), 1) < 0)
+ return;
+
+ local->fw_ap = 0;
+ id = le16_to_cpu(comp.id);
+ if (id != HFA384X_COMP_ID_STA) {
+ if (id == HFA384X_COMP_ID_FW_AP)
+ local->fw_ap = 1;
+ return;
+ }
+
+ major = __le16_to_cpu(comp.major);
+ minor = __le16_to_cpu(comp.minor);
+ variant = __le16_to_cpu(comp.variant);
+ local->sta_fw_ver = PRISM2_FW_VER(major, minor, variant);
+
+ /* Station firmware versions before 1.4.x seem to have a bug in
+ * firmware-based WEP encryption when using Host AP mode, so use
+ * host_encrypt as a default for them. Firmware version 1.4.9 is the
+ * first one that has been seen to produce correct encryption, but the
+ * bug might be fixed before that (although, at least 1.4.2 is broken).
+ */
+ local->fw_encrypt_ok = local->sta_fw_ver >= PRISM2_FW_VER(1,4,9);
+
+ if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt &&
+ !local->fw_encrypt_ok) {
+ printk(KERN_DEBUG "%s: defaulting to host-based encryption as "
+ "a workaround for firmware bug in Host AP mode WEP\n",
+ local->dev->name);
+ local->host_encrypt = 1;
+ }
+
+ /* IEEE 802.11 standard compliant WDS frames (4 addresses) were broken
+ * in station firmware versions before 1.5.x. With these versions, the
+ * driver uses a workaround with bogus frame format (4th address after
+ * the payload). This is not compatible with other AP devices. Since
+ * the firmware bug is fixed in the latest station firmware versions,
+ * automatically enable standard compliant mode for cards using station
+ * firmware version 1.5.0 or newer. */
+ if (local->sta_fw_ver >= PRISM2_FW_VER(1,5,0))
+ local->wds_type |= HOSTAP_WDS_STANDARD_FRAME;
+ else {
+ printk(KERN_DEBUG "%s: defaulting to bogus WDS frame as a "
+ "workaround for firmware bug in Host AP mode WDS\n",
+ local->dev->name);
+ }
+
+ hostap_check_sta_fw_version(local->ap, local->sta_fw_ver);
+}
+
+
+static void prism2_crypt_deinit_entries(local_info_t *local, int force)
+{
+ struct list_head *ptr, *n;
+ struct ieee80211_crypt_data *entry;
+
+ for (ptr = local->crypt_deinit_list.next, n = ptr->next;
+ ptr != &local->crypt_deinit_list; ptr = n, n = ptr->next) {
+ entry = list_entry(ptr, struct ieee80211_crypt_data, list);
+
+ if (atomic_read(&entry->refcnt) != 0 && !force)
+ continue;
+
+ list_del(ptr);
+
+ if (entry->ops)
+ entry->ops->deinit(entry->priv);
+ kfree(entry);
+ }
+}
+
+
+static void prism2_crypt_deinit_handler(unsigned long data)
+{
+ local_info_t *local = (local_info_t *) data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_crypt_deinit_entries(local, 0);
+ if (!list_empty(&local->crypt_deinit_list)) {
+ printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
+ "deletion list\n", local->dev->name);
+ local->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&local->crypt_deinit_timer);
+ }
+ spin_unlock_irqrestore(&local->lock, flags);
+
+}
+
+
+static void hostap_passive_scan(unsigned long data)
+{
+ local_info_t *local = (local_info_t *) data;
+ struct net_device *dev = local->dev;
+ u16 channel;
+
+ if (local->passive_scan_interval <= 0)
+ return;
+
+ if (local->passive_scan_state == PASSIVE_SCAN_LISTEN) {
+ int max_tries = 16;
+
+ /* Even though host system does not really know when the WLAN
+ * MAC is sending frames, try to avoid changing channels for
+ * passive scanning when a host-generated frame is being
+ * transmitted */
+ if (test_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) {
+ printk(KERN_DEBUG "%s: passive scan detected pending "
+ "TX - delaying\n", dev->name);
+ local->passive_scan_timer.expires = jiffies + HZ / 10;
+ add_timer(&local->passive_scan_timer);
+ return;
+ }
+
+ do {
+ local->passive_scan_channel++;
+ if (local->passive_scan_channel > 14)
+ local->passive_scan_channel = 1;
+ max_tries--;
+ } while (!(local->channel_mask &
+ (1 << (local->passive_scan_channel - 1))) &&
+ max_tries > 0);
+
+ if (max_tries == 0) {
+ printk(KERN_INFO "%s: no allowed passive scan channels"
+ " found\n", dev->name);
+ return;
+ }
+
+ printk(KERN_DEBUG "%s: passive scan channel %d\n",
+ dev->name, local->passive_scan_channel);
+ channel = local->passive_scan_channel;
+ local->passive_scan_state = PASSIVE_SCAN_WAIT;
+ local->passive_scan_timer.expires = jiffies + HZ / 10;
+ } else {
+ channel = local->channel;
+ local->passive_scan_state = PASSIVE_SCAN_LISTEN;
+ local->passive_scan_timer.expires = jiffies +
+ local->passive_scan_interval * HZ;
+ }
+
+ if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_TEST |
+ (HFA384X_TEST_CHANGE_CHANNEL << 8),
+ channel, NULL, 0))
+ printk(KERN_ERR "%s: passive scan channel set %d "
+ "failed\n", dev->name, channel);
+
+ add_timer(&local->passive_scan_timer);
+}
+
+
+/* Called only as a scheduled task when communications quality values should
+ * be updated. */
+static void handle_comms_qual_update(void *data)
+{
+ local_info_t *local = data;
+ prism2_update_comms_qual(local->dev);
+}
+
+
+/* Software watchdog - called as a timer. Hardware interrupt (Tick event) is
+ * used to monitor that local->last_tick_timer is being updated. If not,
+ * interrupt busy-loop is assumed and driver tries to recover by masking out
+ * some events. */
+static void hostap_tick_timer(unsigned long data)
+{
+ static unsigned long last_inquire = 0;
+ local_info_t *local = (local_info_t *) data;
+ local->last_tick_timer = jiffies;
+
+ /* Inquire CommTallies every 10 seconds to keep the statistics updated
+ * more often during low load and when using 32-bit tallies. */
+ if ((!last_inquire || time_after(jiffies, last_inquire + 10 * HZ)) &&
+ !local->hw_downloading && local->hw_ready &&
+ !local->hw_resetting && local->dev_enabled) {
+ hfa384x_cmd_callback(local->dev, HFA384X_CMDCODE_INQUIRE,
+ HFA384X_INFO_COMMTALLIES, NULL, 0);
+ last_inquire = jiffies;
+ }
+
+ if ((local->last_comms_qual_update == 0 ||
+ time_after(jiffies, local->last_comms_qual_update + 10 * HZ)) &&
+ (local->iw_mode == IW_MODE_INFRA ||
+ local->iw_mode == IW_MODE_ADHOC)) {
+ schedule_work(&local->comms_qual_update);
+ }
+
+ local->tick_timer.expires = jiffies + 2 * HZ;
+ add_timer(&local->tick_timer);
+}
+
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+static int prism2_registers_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ local_info_t *local = (local_info_t *) data;
+
+ if (off != 0) {
+ *eof = 1;
+ return 0;
+ }
+
+#define SHOW_REG(n) \
+p += sprintf(p, #n "=%04x\n", hfa384x_read_reg(local->dev, HFA384X_##n##_OFF))
+
+ SHOW_REG(CMD);
+ SHOW_REG(PARAM0);
+ SHOW_REG(PARAM1);
+ SHOW_REG(PARAM2);
+ SHOW_REG(STATUS);
+ SHOW_REG(RESP0);
+ SHOW_REG(RESP1);
+ SHOW_REG(RESP2);
+ SHOW_REG(INFOFID);
+ SHOW_REG(CONTROL);
+ SHOW_REG(SELECT0);
+ SHOW_REG(SELECT1);
+ SHOW_REG(OFFSET0);
+ SHOW_REG(OFFSET1);
+ SHOW_REG(RXFID);
+ SHOW_REG(ALLOCFID);
+ SHOW_REG(TXCOMPLFID);
+ SHOW_REG(SWSUPPORT0);
+ SHOW_REG(SWSUPPORT1);
+ SHOW_REG(SWSUPPORT2);
+ SHOW_REG(EVSTAT);
+ SHOW_REG(INTEN);
+ SHOW_REG(EVACK);
+ /* Do not read data registers, because they change the state of the
+ * MAC (offset += 2) */
+ /* SHOW_REG(DATA0); */
+ /* SHOW_REG(DATA1); */
+ SHOW_REG(AUXPAGE);
+ SHOW_REG(AUXOFFSET);
+ /* SHOW_REG(AUXDATA); */
+#ifdef PRISM2_PCI
+ SHOW_REG(PCICOR);
+ SHOW_REG(PCIHCR);
+ SHOW_REG(PCI_M0_ADDRH);
+ SHOW_REG(PCI_M0_ADDRL);
+ SHOW_REG(PCI_M0_LEN);
+ SHOW_REG(PCI_M0_CTL);
+ SHOW_REG(PCI_STATUS);
+ SHOW_REG(PCI_M1_ADDRH);
+ SHOW_REG(PCI_M1_ADDRL);
+ SHOW_REG(PCI_M1_LEN);
+ SHOW_REG(PCI_M1_CTL);
+#endif /* PRISM2_PCI */
+
+ return (p - page);
+}
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+
+struct set_tim_data {
+ struct list_head list;
+ int aid;
+ int set;
+};
+
+static int prism2_set_tim(struct net_device *dev, int aid, int set)
+{
+ struct list_head *ptr;
+ struct set_tim_data *new_entry;
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ new_entry = (struct set_tim_data *)
+ kmalloc(sizeof(*new_entry), GFP_ATOMIC);
+ if (new_entry == NULL) {
+ printk(KERN_DEBUG "%s: prism2_set_tim: kmalloc failed\n",
+ local->dev->name);
+ return -ENOMEM;
+ }
+ memset(new_entry, 0, sizeof(*new_entry));
+ new_entry->aid = aid;
+ new_entry->set = set;
+
+ spin_lock_bh(&local->set_tim_lock);
+ list_for_each(ptr, &local->set_tim_list) {
+ struct set_tim_data *entry =
+ list_entry(ptr, struct set_tim_data, list);
+ if (entry->aid == aid) {
+ PDEBUG(DEBUG_PS2, "%s: prism2_set_tim: aid=%d "
+ "set=%d ==> %d\n",
+ local->dev->name, aid, entry->set, set);
+ entry->set = set;
+ kfree(new_entry);
+ new_entry = NULL;
+ break;
+ }
+ }
+ if (new_entry)
+ list_add_tail(&new_entry->list, &local->set_tim_list);
+ spin_unlock_bh(&local->set_tim_lock);
+
+ schedule_work(&local->set_tim_queue);
+
+ return 0;
+}
+
+
+static void handle_set_tim_queue(void *data)
+{
+ local_info_t *local = (local_info_t *) data;
+ struct set_tim_data *entry;
+ u16 val;
+
+ for (;;) {
+ entry = NULL;
+ spin_lock_bh(&local->set_tim_lock);
+ if (!list_empty(&local->set_tim_list)) {
+ entry = list_entry(local->set_tim_list.next,
+ struct set_tim_data, list);
+ list_del(&entry->list);
+ }
+ spin_unlock_bh(&local->set_tim_lock);
+ if (!entry)
+ break;
+
+ PDEBUG(DEBUG_PS2, "%s: handle_set_tim_queue: aid=%d set=%d\n",
+ local->dev->name, entry->aid, entry->set);
+
+ val = entry->aid;
+ if (entry->set)
+ val |= 0x8000;
+ if (hostap_set_word(local->dev, HFA384X_RID_CNFTIMCTRL, val)) {
+ printk(KERN_DEBUG "%s: set_tim failed (aid=%d "
+ "set=%d)\n",
+ local->dev->name, entry->aid, entry->set);
+ }
+
+ kfree(entry);
+ }
+}
+
+
+static void prism2_clear_set_tim_queue(local_info_t *local)
+{
+ struct list_head *ptr, *n;
+
+ list_for_each_safe(ptr, n, &local->set_tim_list) {
+ struct set_tim_data *entry;
+ entry = list_entry(ptr, struct set_tim_data, list);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+}
+
+
+static struct net_device *
+prism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx,
+ struct device *sdev)
+{
+ struct net_device *dev;
+ struct hostap_interface *iface;
+ struct local_info *local;
+ int len, i, ret;
+
+ if (funcs == NULL)
+ return NULL;
+
+ len = strlen(dev_template);
+ if (len >= IFNAMSIZ || strstr(dev_template, "%d") == NULL) {
+ printk(KERN_WARNING "hostap: Invalid dev_template='%s'\n",
+ dev_template);
+ return NULL;
+ }
+
+ len = sizeof(struct hostap_interface) +
+ 3 + sizeof(struct local_info) +
+ 3 + sizeof(struct ap_data);
+
+ dev = alloc_etherdev(len);
+ if (dev == NULL)
+ return NULL;
+
+ iface = netdev_priv(dev);
+ local = (struct local_info *) ((((long) (iface + 1)) + 3) & ~3);
+ local->ap = (struct ap_data *) ((((long) (local + 1)) + 3) & ~3);
+ local->dev = iface->dev = dev;
+ iface->local = local;
+ iface->type = HOSTAP_INTERFACE_MASTER;
+ INIT_LIST_HEAD(&local->hostap_interfaces);
+
+ local->hw_module = THIS_MODULE;
+
+#ifdef PRISM2_IO_DEBUG
+ local->io_debug_enabled = 1;
+#endif /* PRISM2_IO_DEBUG */
+
+ local->func = funcs;
+ local->func->cmd = hfa384x_cmd;
+ local->func->read_regs = hfa384x_read_regs;
+ local->func->get_rid = hfa384x_get_rid;
+ local->func->set_rid = hfa384x_set_rid;
+ local->func->hw_enable = prism2_hw_enable;
+ local->func->hw_config = prism2_hw_config;
+ local->func->hw_reset = prism2_hw_reset;
+ local->func->hw_shutdown = prism2_hw_shutdown;
+ local->func->reset_port = prism2_reset_port;
+ local->func->schedule_reset = prism2_schedule_reset;
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+ local->func->read_aux = prism2_download_aux_dump;
+ local->func->download = prism2_download;
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+ local->func->tx = prism2_tx_80211;
+ local->func->set_tim = prism2_set_tim;
+ local->func->need_tx_headroom = 0; /* no need to add txdesc in
+ * skb->data (FIX: maybe for DMA bus
+ * mastering? */
+
+ local->mtu = mtu;
+
+ rwlock_init(&local->iface_lock);
+ spin_lock_init(&local->txfidlock);
+ spin_lock_init(&local->cmdlock);
+ spin_lock_init(&local->baplock);
+ spin_lock_init(&local->lock);
+ init_MUTEX(&local->rid_bap_sem);
+
+ if (card_idx < 0 || card_idx >= MAX_PARM_DEVICES)
+ card_idx = 0;
+ local->card_idx = card_idx;
+
+ len = strlen(essid);
+ memcpy(local->essid, essid,
+ len > MAX_SSID_LEN ? MAX_SSID_LEN : len);
+ local->essid[MAX_SSID_LEN] = '\0';
+ i = GET_INT_PARM(iw_mode, card_idx);
+ if ((i >= IW_MODE_ADHOC && i <= IW_MODE_REPEAT) ||
+ i == IW_MODE_MONITOR) {
+ local->iw_mode = i;
+ } else {
+ printk(KERN_WARNING "prism2: Unknown iw_mode %d; using "
+ "IW_MODE_MASTER\n", i);
+ local->iw_mode = IW_MODE_MASTER;
+ }
+ local->channel = GET_INT_PARM(channel, card_idx);
+ local->beacon_int = GET_INT_PARM(beacon_int, card_idx);
+ local->dtim_period = GET_INT_PARM(dtim_period, card_idx);
+ local->wds_max_connections = 16;
+ local->tx_control = HFA384X_TX_CTRL_FLAGS;
+ local->manual_retry_count = -1;
+ local->rts_threshold = 2347;
+ local->fragm_threshold = 2346;
+ local->rssi_to_dBm = 100; /* default; to be overriden by
+ * cnfDbmAdjust, if available */
+ local->auth_algs = PRISM2_AUTH_OPEN | PRISM2_AUTH_SHARED_KEY;
+ local->sram_type = -1;
+ local->scan_channel_mask = 0xffff;
+
+ /* Initialize task queue structures */
+ INIT_WORK(&local->reset_queue, handle_reset_queue, local);
+ INIT_WORK(&local->set_multicast_list_queue,
+ hostap_set_multicast_list_queue, local->dev);
+
+ INIT_WORK(&local->set_tim_queue, handle_set_tim_queue, local);
+ INIT_LIST_HEAD(&local->set_tim_list);
+ spin_lock_init(&local->set_tim_lock);
+
+ INIT_WORK(&local->comms_qual_update, handle_comms_qual_update, local);
+
+ /* Initialize tasklets for handling hardware IRQ related operations
+ * outside hw IRQ handler */
+#define HOSTAP_TASKLET_INIT(q, f, d) \
+do { memset((q), 0, sizeof(*(q))); (q)->func = (f); (q)->data = (d); } \
+while (0)
+ HOSTAP_TASKLET_INIT(&local->bap_tasklet, hostap_bap_tasklet,
+ (unsigned long) local);
+
+ HOSTAP_TASKLET_INIT(&local->info_tasklet, hostap_info_tasklet,
+ (unsigned long) local);
+ hostap_info_init(local);
+
+ HOSTAP_TASKLET_INIT(&local->rx_tasklet,
+ hostap_rx_tasklet, (unsigned long) local);
+ skb_queue_head_init(&local->rx_list);
+
+ HOSTAP_TASKLET_INIT(&local->sta_tx_exc_tasklet,
+ hostap_sta_tx_exc_tasklet, (unsigned long) local);
+ skb_queue_head_init(&local->sta_tx_exc_list);
+
+ INIT_LIST_HEAD(&local->cmd_queue);
+ init_waitqueue_head(&local->hostscan_wq);
+ INIT_LIST_HEAD(&local->crypt_deinit_list);
+ init_timer(&local->crypt_deinit_timer);
+ local->crypt_deinit_timer.data = (unsigned long) local;
+ local->crypt_deinit_timer.function = prism2_crypt_deinit_handler;
+
+ init_timer(&local->passive_scan_timer);
+ local->passive_scan_timer.data = (unsigned long) local;
+ local->passive_scan_timer.function = hostap_passive_scan;
+
+ init_timer(&local->tick_timer);
+ local->tick_timer.data = (unsigned long) local;
+ local->tick_timer.function = hostap_tick_timer;
+ local->tick_timer.expires = jiffies + 2 * HZ;
+ add_timer(&local->tick_timer);
+
+ INIT_LIST_HEAD(&local->bss_list);
+
+ hostap_setup_dev(dev, local, 1);
+ local->saved_eth_header_parse = dev->hard_header_parse;
+
+ dev->hard_start_xmit = hostap_master_start_xmit;
+ dev->type = ARPHRD_IEEE80211;
+ dev->hard_header_parse = hostap_80211_header_parse;
+
+ rtnl_lock();
+ ret = dev_alloc_name(dev, "wifi%d");
+ SET_NETDEV_DEV(dev, sdev);
+ if (ret >= 0)
+ ret = register_netdevice(dev);
+ rtnl_unlock();
+ if (ret < 0) {
+ printk(KERN_WARNING "%s: register netdevice failed!\n",
+ dev_info);
+ goto fail;
+ }
+ printk(KERN_INFO "%s: Registered netdevice %s\n", dev_info, dev->name);
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+ create_proc_read_entry("registers", 0, local->proc,
+ prism2_registers_proc_read, local);
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+ hostap_init_data(local);
+ return dev;
+
+ fail:
+ free_netdev(dev);
+ return NULL;
+}
+
+
+static int hostap_hw_ready(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ struct local_info *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+ local->ddev = hostap_add_interface(local, HOSTAP_INTERFACE_MAIN, 0,
+ "", dev_template);
+
+ if (local->ddev) {
+ if (local->iw_mode == IW_MODE_INFRA ||
+ local->iw_mode == IW_MODE_ADHOC) {
+ netif_carrier_off(local->dev);
+ netif_carrier_off(local->ddev);
+ }
+ hostap_init_proc(local);
+ hostap_init_ap_proc(local);
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static void prism2_free_local_data(struct net_device *dev)
+{
+ struct hostap_tx_callback_info *tx_cb, *tx_cb_prev;
+ int i;
+ struct hostap_interface *iface;
+ struct local_info *local;
+ struct list_head *ptr, *n;
+
+ if (dev == NULL)
+ return;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ flush_scheduled_work();
+
+ if (timer_pending(&local->crypt_deinit_timer))
+ del_timer(&local->crypt_deinit_timer);
+ prism2_crypt_deinit_entries(local, 1);
+
+ if (timer_pending(&local->passive_scan_timer))
+ del_timer(&local->passive_scan_timer);
+
+ if (timer_pending(&local->tick_timer))
+ del_timer(&local->tick_timer);
+
+ prism2_clear_cmd_queue(local);
+
+ skb_queue_purge(&local->info_list);
+ skb_queue_purge(&local->rx_list);
+ skb_queue_purge(&local->sta_tx_exc_list);
+
+ if (local->dev_enabled)
+ prism2_callback(local, PRISM2_CALLBACK_DISABLE);
+
+ for (i = 0; i < WEP_KEYS; i++) {
+ struct ieee80211_crypt_data *crypt = local->crypt[i];
+ if (crypt) {
+ if (crypt->ops)
+ crypt->ops->deinit(crypt->priv);
+ kfree(crypt);
+ local->crypt[i] = NULL;
+ }
+ }
+
+ if (local->ap != NULL)
+ hostap_free_data(local->ap);
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+ if (local->proc != NULL)
+ remove_proc_entry("registers", local->proc);
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+ hostap_remove_proc(local);
+
+ tx_cb = local->tx_callback;
+ while (tx_cb != NULL) {
+ tx_cb_prev = tx_cb;
+ tx_cb = tx_cb->next;
+ kfree(tx_cb_prev);
+ }
+
+ hostap_set_hostapd(local, 0, 0);
+ hostap_set_hostapd_sta(local, 0, 0);
+
+ for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) {
+ if (local->frag_cache[i].skb != NULL)
+ dev_kfree_skb(local->frag_cache[i].skb);
+ }
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+ prism2_download_free_data(local->dl_pri);
+ prism2_download_free_data(local->dl_sec);
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+ list_for_each_safe(ptr, n, &local->hostap_interfaces) {
+ iface = list_entry(ptr, struct hostap_interface, list);
+ if (iface->type == HOSTAP_INTERFACE_MASTER) {
+ /* special handling for this interface below */
+ continue;
+ }
+ hostap_remove_interface(iface->dev, 0, 1);
+ }
+
+ prism2_clear_set_tim_queue(local);
+
+ list_for_each_safe(ptr, n, &local->bss_list) {
+ struct hostap_bss_info *bss =
+ list_entry(ptr, struct hostap_bss_info, list);
+ kfree(bss);
+ }
+
+ kfree(local->pda);
+ kfree(local->last_scan_results);
+ kfree(local->generic_elem);
+
+ unregister_netdev(local->dev);
+ free_netdev(local->dev);
+}
+
+
+#ifndef PRISM2_PLX
+static void prism2_suspend(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ struct local_info *local;
+ union iwreq_data wrqu;
+
+ iface = dev->priv;
+ local = iface->local;
+
+ /* Send disconnect event, e.g., to trigger reassociation after resume
+ * if wpa_supplicant is used. */
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL);
+
+ /* Disable hardware and firmware */
+ prism2_hw_shutdown(dev, 0);
+}
+#endif /* PRISM2_PLX */
+
+
+/* These might at some point be compiled separately and used as separate
+ * kernel modules or linked into one */
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+#include "hostap_download.c"
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+#ifdef PRISM2_CALLBACK
+/* External hostap_callback.c file can be used to, e.g., blink activity led.
+ * This can use platform specific code and must define prism2_callback()
+ * function (if PRISM2_CALLBACK is not defined, these function calls are not
+ * used. */
+#include "hostap_callback.c"
+#endif /* PRISM2_CALLBACK */
diff --git a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/hostap/hostap_info.c
new file mode 100644
index 000000000000..5aa998fdf1c4
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_info.c
@@ -0,0 +1,499 @@
+/* Host AP driver Info Frame processing (part of hostap.o module) */
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_info_commtallies16(local_info_t *local, unsigned char *buf,
+ int left)
+{
+ struct hfa384x_comm_tallies *tallies;
+
+ if (left < sizeof(struct hfa384x_comm_tallies)) {
+ printk(KERN_DEBUG "%s: too short (len=%d) commtallies "
+ "info frame\n", local->dev->name, left);
+ return;
+ }
+
+ tallies = (struct hfa384x_comm_tallies *) buf;
+#define ADD_COMM_TALLIES(name) \
+local->comm_tallies.name += le16_to_cpu(tallies->name)
+ ADD_COMM_TALLIES(tx_unicast_frames);
+ ADD_COMM_TALLIES(tx_multicast_frames);
+ ADD_COMM_TALLIES(tx_fragments);
+ ADD_COMM_TALLIES(tx_unicast_octets);
+ ADD_COMM_TALLIES(tx_multicast_octets);
+ ADD_COMM_TALLIES(tx_deferred_transmissions);
+ ADD_COMM_TALLIES(tx_single_retry_frames);
+ ADD_COMM_TALLIES(tx_multiple_retry_frames);
+ ADD_COMM_TALLIES(tx_retry_limit_exceeded);
+ ADD_COMM_TALLIES(tx_discards);
+ ADD_COMM_TALLIES(rx_unicast_frames);
+ ADD_COMM_TALLIES(rx_multicast_frames);
+ ADD_COMM_TALLIES(rx_fragments);
+ ADD_COMM_TALLIES(rx_unicast_octets);
+ ADD_COMM_TALLIES(rx_multicast_octets);
+ ADD_COMM_TALLIES(rx_fcs_errors);
+ ADD_COMM_TALLIES(rx_discards_no_buffer);
+ ADD_COMM_TALLIES(tx_discards_wrong_sa);
+ ADD_COMM_TALLIES(rx_discards_wep_undecryptable);
+ ADD_COMM_TALLIES(rx_message_in_msg_fragments);
+ ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments);
+#undef ADD_COMM_TALLIES
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_info_commtallies32(local_info_t *local, unsigned char *buf,
+ int left)
+{
+ struct hfa384x_comm_tallies32 *tallies;
+
+ if (left < sizeof(struct hfa384x_comm_tallies32)) {
+ printk(KERN_DEBUG "%s: too short (len=%d) commtallies32 "
+ "info frame\n", local->dev->name, left);
+ return;
+ }
+
+ tallies = (struct hfa384x_comm_tallies32 *) buf;
+#define ADD_COMM_TALLIES(name) \
+local->comm_tallies.name += le32_to_cpu(tallies->name)
+ ADD_COMM_TALLIES(tx_unicast_frames);
+ ADD_COMM_TALLIES(tx_multicast_frames);
+ ADD_COMM_TALLIES(tx_fragments);
+ ADD_COMM_TALLIES(tx_unicast_octets);
+ ADD_COMM_TALLIES(tx_multicast_octets);
+ ADD_COMM_TALLIES(tx_deferred_transmissions);
+ ADD_COMM_TALLIES(tx_single_retry_frames);
+ ADD_COMM_TALLIES(tx_multiple_retry_frames);
+ ADD_COMM_TALLIES(tx_retry_limit_exceeded);
+ ADD_COMM_TALLIES(tx_discards);
+ ADD_COMM_TALLIES(rx_unicast_frames);
+ ADD_COMM_TALLIES(rx_multicast_frames);
+ ADD_COMM_TALLIES(rx_fragments);
+ ADD_COMM_TALLIES(rx_unicast_octets);
+ ADD_COMM_TALLIES(rx_multicast_octets);
+ ADD_COMM_TALLIES(rx_fcs_errors);
+ ADD_COMM_TALLIES(rx_discards_no_buffer);
+ ADD_COMM_TALLIES(tx_discards_wrong_sa);
+ ADD_COMM_TALLIES(rx_discards_wep_undecryptable);
+ ADD_COMM_TALLIES(rx_message_in_msg_fragments);
+ ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments);
+#undef ADD_COMM_TALLIES
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_info_commtallies(local_info_t *local, unsigned char *buf,
+ int left)
+{
+ if (local->tallies32)
+ prism2_info_commtallies32(local, buf, left);
+ else
+ prism2_info_commtallies16(local, buf, left);
+}
+
+
+#ifndef PRISM2_NO_STATION_MODES
+#ifndef PRISM2_NO_DEBUG
+static const char* hfa384x_linkstatus_str(u16 linkstatus)
+{
+ switch (linkstatus) {
+ case HFA384X_LINKSTATUS_CONNECTED:
+ return "Connected";
+ case HFA384X_LINKSTATUS_DISCONNECTED:
+ return "Disconnected";
+ case HFA384X_LINKSTATUS_AP_CHANGE:
+ return "Access point change";
+ case HFA384X_LINKSTATUS_AP_OUT_OF_RANGE:
+ return "Access point out of range";
+ case HFA384X_LINKSTATUS_AP_IN_RANGE:
+ return "Access point in range";
+ case HFA384X_LINKSTATUS_ASSOC_FAILED:
+ return "Association failed";
+ default:
+ return "Unknown";
+ }
+}
+#endif /* PRISM2_NO_DEBUG */
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_info_linkstatus(local_info_t *local, unsigned char *buf,
+ int left)
+{
+ u16 val;
+ int non_sta_mode;
+
+ /* Alloc new JoinRequests to occur since LinkStatus for the previous
+ * has been received */
+ local->last_join_time = 0;
+
+ if (left != 2) {
+ printk(KERN_DEBUG "%s: invalid linkstatus info frame "
+ "length %d\n", local->dev->name, left);
+ return;
+ }
+
+ non_sta_mode = local->iw_mode == IW_MODE_MASTER ||
+ local->iw_mode == IW_MODE_REPEAT ||
+ local->iw_mode == IW_MODE_MONITOR;
+
+ val = buf[0] | (buf[1] << 8);
+ if (!non_sta_mode || val != HFA384X_LINKSTATUS_DISCONNECTED) {
+ PDEBUG(DEBUG_EXTRA, "%s: LinkStatus=%d (%s)\n",
+ local->dev->name, val, hfa384x_linkstatus_str(val));
+ }
+
+ if (non_sta_mode) {
+ netif_carrier_on(local->dev);
+ netif_carrier_on(local->ddev);
+ return;
+ }
+
+ /* Get current BSSID later in scheduled task */
+ set_bit(PRISM2_INFO_PENDING_LINKSTATUS, &local->pending_info);
+ local->prev_link_status = val;
+ schedule_work(&local->info_queue);
+}
+
+
+static void prism2_host_roaming(local_info_t *local)
+{
+ struct hfa384x_join_request req;
+ struct net_device *dev = local->dev;
+ struct hfa384x_hostscan_result *selected, *entry;
+ int i;
+ unsigned long flags;
+
+ if (local->last_join_time &&
+ time_before(jiffies, local->last_join_time + 10 * HZ)) {
+ PDEBUG(DEBUG_EXTRA, "%s: last join request has not yet been "
+ "completed - waiting for it before issuing new one\n",
+ dev->name);
+ return;
+ }
+
+ /* ScanResults are sorted: first ESS results in decreasing signal
+ * quality then IBSS results in similar order.
+ * Trivial roaming policy: just select the first entry.
+ * This could probably be improved by adding hysteresis to limit
+ * number of handoffs, etc.
+ *
+ * Could do periodic RID_SCANREQUEST or Inquire F101 to get new
+ * ScanResults */
+ spin_lock_irqsave(&local->lock, flags);
+ if (local->last_scan_results == NULL ||
+ local->last_scan_results_count == 0) {
+ spin_unlock_irqrestore(&local->lock, flags);
+ PDEBUG(DEBUG_EXTRA, "%s: no scan results for host roaming\n",
+ dev->name);
+ return;
+ }
+
+ selected = &local->last_scan_results[0];
+
+ if (local->preferred_ap[0] || local->preferred_ap[1] ||
+ local->preferred_ap[2] || local->preferred_ap[3] ||
+ local->preferred_ap[4] || local->preferred_ap[5]) {
+ /* Try to find preferred AP */
+ PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID " MACSTR "\n",
+ dev->name, MAC2STR(local->preferred_ap));
+ for (i = 0; i < local->last_scan_results_count; i++) {
+ entry = &local->last_scan_results[i];
+ if (memcmp(local->preferred_ap, entry->bssid, 6) == 0)
+ {
+ PDEBUG(DEBUG_EXTRA, "%s: using preferred AP "
+ "selection\n", dev->name);
+ selected = entry;
+ break;
+ }
+ }
+ }
+
+ memcpy(req.bssid, selected->bssid, 6);
+ req.channel = selected->chid;
+ spin_unlock_irqrestore(&local->lock, flags);
+
+ PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=" MACSTR " channel=%d\n",
+ dev->name, MAC2STR(req.bssid), le16_to_cpu(req.channel));
+ if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req,
+ sizeof(req))) {
+ printk(KERN_DEBUG "%s: JoinRequest failed\n", dev->name);
+ }
+ local->last_join_time = jiffies;
+}
+
+
+static void hostap_report_scan_complete(local_info_t *local)
+{
+ union iwreq_data wrqu;
+
+ /* Inform user space about new scan results (just empty event,
+ * SIOCGIWSCAN can be used to fetch data */
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(local->dev, SIOCGIWSCAN, &wrqu, NULL);
+
+ /* Allow SIOCGIWSCAN handling to occur since we have received
+ * scanning result */
+ local->scan_timestamp = 0;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_info_scanresults(local_info_t *local, unsigned char *buf,
+ int left)
+{
+ u16 *pos;
+ int new_count, i;
+ unsigned long flags;
+ struct hfa384x_scan_result *res;
+ struct hfa384x_hostscan_result *results, *prev;
+
+ if (left < 4) {
+ printk(KERN_DEBUG "%s: invalid scanresult info frame "
+ "length %d\n", local->dev->name, left);
+ return;
+ }
+
+ pos = (u16 *) buf;
+ pos++;
+ pos++;
+ left -= 4;
+
+ new_count = left / sizeof(struct hfa384x_scan_result);
+ results = kmalloc(new_count * sizeof(struct hfa384x_hostscan_result),
+ GFP_ATOMIC);
+ if (results == NULL)
+ return;
+
+ /* Convert to hostscan result format. */
+ res = (struct hfa384x_scan_result *) pos;
+ for (i = 0; i < new_count; i++) {
+ memcpy(&results[i], &res[i],
+ sizeof(struct hfa384x_scan_result));
+ results[i].atim = 0;
+ }
+
+ spin_lock_irqsave(&local->lock, flags);
+ local->last_scan_type = PRISM2_SCAN;
+ prev = local->last_scan_results;
+ local->last_scan_results = results;
+ local->last_scan_results_count = new_count;
+ spin_unlock_irqrestore(&local->lock, flags);
+ kfree(prev);
+
+ hostap_report_scan_complete(local);
+
+ /* Perform rest of ScanResults handling later in scheduled task */
+ set_bit(PRISM2_INFO_PENDING_SCANRESULTS, &local->pending_info);
+ schedule_work(&local->info_queue);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void prism2_info_hostscanresults(local_info_t *local,
+ unsigned char *buf, int left)
+{
+ int i, result_size, copy_len, new_count;
+ struct hfa384x_hostscan_result *results, *prev;
+ unsigned long flags;
+ u16 *pos;
+ u8 *ptr;
+
+ wake_up_interruptible(&local->hostscan_wq);
+
+ if (left < 4) {
+ printk(KERN_DEBUG "%s: invalid hostscanresult info frame "
+ "length %d\n", local->dev->name, left);
+ return;
+ }
+
+ pos = (u16 *) buf;
+ copy_len = result_size = le16_to_cpu(*pos);
+ if (result_size == 0) {
+ printk(KERN_DEBUG "%s: invalid result_size (0) in "
+ "hostscanresults\n", local->dev->name);
+ return;
+ }
+ if (copy_len > sizeof(struct hfa384x_hostscan_result))
+ copy_len = sizeof(struct hfa384x_hostscan_result);
+
+ pos++;
+ pos++;
+ left -= 4;
+ ptr = (u8 *) pos;
+
+ new_count = left / result_size;
+ results = kmalloc(new_count * sizeof(struct hfa384x_hostscan_result),
+ GFP_ATOMIC);
+ if (results == NULL)
+ return;
+ memset(results, 0, new_count * sizeof(struct hfa384x_hostscan_result));
+
+ for (i = 0; i < new_count; i++) {
+ memcpy(&results[i], ptr, copy_len);
+ ptr += result_size;
+ left -= result_size;
+ }
+
+ if (left) {
+ printk(KERN_DEBUG "%s: short HostScan result entry (%d/%d)\n",
+ local->dev->name, left, result_size);
+ }
+
+ spin_lock_irqsave(&local->lock, flags);
+ local->last_scan_type = PRISM2_HOSTSCAN;
+ prev = local->last_scan_results;
+ local->last_scan_results = results;
+ local->last_scan_results_count = new_count;
+ spin_unlock_irqrestore(&local->lock, flags);
+ kfree(prev);
+
+ hostap_report_scan_complete(local);
+}
+#endif /* PRISM2_NO_STATION_MODES */
+
+
+/* Called only as a tasklet (software IRQ) */
+void hostap_info_process(local_info_t *local, struct sk_buff *skb)
+{
+ struct hfa384x_info_frame *info;
+ unsigned char *buf;
+ int left;
+#ifndef PRISM2_NO_DEBUG
+ int i;
+#endif /* PRISM2_NO_DEBUG */
+
+ info = (struct hfa384x_info_frame *) skb->data;
+ buf = skb->data + sizeof(*info);
+ left = skb->len - sizeof(*info);
+
+ switch (info->type) {
+ case HFA384X_INFO_COMMTALLIES:
+ prism2_info_commtallies(local, buf, left);
+ break;
+
+#ifndef PRISM2_NO_STATION_MODES
+ case HFA384X_INFO_LINKSTATUS:
+ prism2_info_linkstatus(local, buf, left);
+ break;
+
+ case HFA384X_INFO_SCANRESULTS:
+ prism2_info_scanresults(local, buf, left);
+ break;
+
+ case HFA384X_INFO_HOSTSCANRESULTS:
+ prism2_info_hostscanresults(local, buf, left);
+ break;
+#endif /* PRISM2_NO_STATION_MODES */
+
+#ifndef PRISM2_NO_DEBUG
+ default:
+ PDEBUG(DEBUG_EXTRA, "%s: INFO - len=%d type=0x%04x\n",
+ local->dev->name, info->len, info->type);
+ PDEBUG(DEBUG_EXTRA, "Unknown info frame:");
+ for (i = 0; i < (left < 100 ? left : 100); i++)
+ PDEBUG2(DEBUG_EXTRA, " %02x", buf[i]);
+ PDEBUG2(DEBUG_EXTRA, "\n");
+ break;
+#endif /* PRISM2_NO_DEBUG */
+ }
+}
+
+
+#ifndef PRISM2_NO_STATION_MODES
+static void handle_info_queue_linkstatus(local_info_t *local)
+{
+ int val = local->prev_link_status;
+ int connected;
+ union iwreq_data wrqu;
+
+ connected =
+ val == HFA384X_LINKSTATUS_CONNECTED ||
+ val == HFA384X_LINKSTATUS_AP_CHANGE ||
+ val == HFA384X_LINKSTATUS_AP_IN_RANGE;
+
+ if (local->func->get_rid(local->dev, HFA384X_RID_CURRENTBSSID,
+ local->bssid, ETH_ALEN, 1) < 0) {
+ printk(KERN_DEBUG "%s: could not read CURRENTBSSID after "
+ "LinkStatus event\n", local->dev->name);
+ } else {
+ PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID=" MACSTR "\n",
+ local->dev->name,
+ MAC2STR((unsigned char *) local->bssid));
+ if (local->wds_type & HOSTAP_WDS_AP_CLIENT)
+ hostap_add_sta(local->ap, local->bssid);
+ }
+
+ /* Get BSSID if we have a valid AP address */
+ if (connected) {
+ netif_carrier_on(local->dev);
+ netif_carrier_on(local->ddev);
+ memcpy(wrqu.ap_addr.sa_data, local->bssid, ETH_ALEN);
+ } else {
+ netif_carrier_off(local->dev);
+ netif_carrier_off(local->ddev);
+ memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+ }
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+ /*
+ * Filter out sequential disconnect events in order not to cause a
+ * flood of SIOCGIWAP events that have a race condition with EAPOL
+ * frames and can confuse wpa_supplicant about the current association
+ * status.
+ */
+ if (connected || local->prev_linkstatus_connected)
+ wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL);
+ local->prev_linkstatus_connected = connected;
+}
+
+
+static void handle_info_queue_scanresults(local_info_t *local)
+{
+ if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA)
+ prism2_host_roaming(local);
+
+ if (local->host_roaming == 2 && local->iw_mode == IW_MODE_INFRA &&
+ memcmp(local->preferred_ap, "\x00\x00\x00\x00\x00\x00",
+ ETH_ALEN) != 0) {
+ /*
+ * Firmware seems to be getting into odd state in host_roaming
+ * mode 2 when hostscan is used without join command, so try
+ * to fix this by re-joining the current AP. This does not
+ * actually trigger a new association if the current AP is
+ * still in the scan results.
+ */
+ prism2_host_roaming(local);
+ }
+}
+
+
+/* Called only as scheduled task after receiving info frames (used to avoid
+ * pending too much time in HW IRQ handler). */
+static void handle_info_queue(void *data)
+{
+ local_info_t *local = (local_info_t *) data;
+
+ if (test_and_clear_bit(PRISM2_INFO_PENDING_LINKSTATUS,
+ &local->pending_info))
+ handle_info_queue_linkstatus(local);
+
+ if (test_and_clear_bit(PRISM2_INFO_PENDING_SCANRESULTS,
+ &local->pending_info))
+ handle_info_queue_scanresults(local);
+}
+#endif /* PRISM2_NO_STATION_MODES */
+
+
+void hostap_info_init(local_info_t *local)
+{
+ skb_queue_head_init(&local->info_list);
+#ifndef PRISM2_NO_STATION_MODES
+ INIT_WORK(&local->info_queue, handle_info_queue, local);
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+
+EXPORT_SYMBOL(hostap_info_init);
+EXPORT_SYMBOL(hostap_info_process);
diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c
new file mode 100644
index 000000000000..e720369a3515
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_ioctl.c
@@ -0,0 +1,4102 @@
+/* ioctl() (mostly Linux Wireless Extensions) routines for Host AP driver */
+
+#ifdef in_atomic
+/* Get kernel_locked() for in_atomic() */
+#include <linux/smp_lock.h>
+#endif
+#include <linux/ethtool.h>
+
+
+static struct iw_statistics *hostap_get_wireless_stats(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct iw_statistics *wstats;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ /* Why are we doing that ? Jean II */
+ if (iface->type != HOSTAP_INTERFACE_MAIN)
+ return NULL;
+
+ wstats = &local->wstats;
+
+ wstats->status = 0;
+ wstats->discard.code =
+ local->comm_tallies.rx_discards_wep_undecryptable;
+ wstats->discard.misc =
+ local->comm_tallies.rx_fcs_errors +
+ local->comm_tallies.rx_discards_no_buffer +
+ local->comm_tallies.tx_discards_wrong_sa;
+
+ wstats->discard.retries =
+ local->comm_tallies.tx_retry_limit_exceeded;
+ wstats->discard.fragment =
+ local->comm_tallies.rx_message_in_bad_msg_fragments;
+
+ if (local->iw_mode != IW_MODE_MASTER &&
+ local->iw_mode != IW_MODE_REPEAT) {
+ int update = 1;
+#ifdef in_atomic
+ /* RID reading might sleep and it must not be called in
+ * interrupt context or while atomic. However, this
+ * function seems to be called while atomic (at least in Linux
+ * 2.5.59). Update signal quality values only if in suitable
+ * context. Otherwise, previous values read from tick timer
+ * will be used. */
+ if (in_atomic())
+ update = 0;
+#endif /* in_atomic */
+
+ if (update && prism2_update_comms_qual(dev) == 0)
+ wstats->qual.updated = 7;
+
+ wstats->qual.qual = local->comms_qual;
+ wstats->qual.level = local->avg_signal;
+ wstats->qual.noise = local->avg_noise;
+ } else {
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ wstats->qual.noise = 0;
+ wstats->qual.updated = 0;
+ }
+
+ return wstats;
+}
+
+
+static int prism2_get_datarates(struct net_device *dev, u8 *rates)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u8 buf[12];
+ int len;
+ u16 val;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ len = local->func->get_rid(dev, HFA384X_RID_SUPPORTEDDATARATES, buf,
+ sizeof(buf), 0);
+ if (len < 2)
+ return 0;
+
+ val = le16_to_cpu(*(u16 *) buf); /* string length */
+
+ if (len - 2 < val || val > 10)
+ return 0;
+
+ memcpy(rates, buf + 2, val);
+ return val;
+}
+
+
+static int prism2_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ char *name, char *extra)
+{
+ u8 rates[10];
+ int len, i, over2 = 0;
+
+ len = prism2_get_datarates(dev, rates);
+
+ for (i = 0; i < len; i++) {
+ if (rates[i] == 0x0b || rates[i] == 0x16) {
+ over2 = 1;
+ break;
+ }
+ }
+
+ strcpy(name, over2 ? "IEEE 802.11b" : "IEEE 802.11-DS");
+
+ return 0;
+}
+
+
+static void prism2_crypt_delayed_deinit(local_info_t *local,
+ struct ieee80211_crypt_data **crypt)
+{
+ struct ieee80211_crypt_data *tmp;
+ unsigned long flags;
+
+ tmp = *crypt;
+ *crypt = NULL;
+
+ if (tmp == NULL)
+ return;
+
+ /* must not run ops->deinit() while there may be pending encrypt or
+ * decrypt operations. Use a list of delayed deinits to avoid needing
+ * locking. */
+
+ spin_lock_irqsave(&local->lock, flags);
+ list_add(&tmp->list, &local->crypt_deinit_list);
+ if (!timer_pending(&local->crypt_deinit_timer)) {
+ local->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&local->crypt_deinit_timer);
+ }
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+
+static int prism2_ioctl_siwencode(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq, char *keybuf)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int i;
+ struct ieee80211_crypt_data **crypt;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ i = erq->flags & IW_ENCODE_INDEX;
+ if (i < 1 || i > 4)
+ i = local->tx_keyidx;
+ else
+ i--;
+ if (i < 0 || i >= WEP_KEYS)
+ return -EINVAL;
+
+ crypt = &local->crypt[i];
+
+ if (erq->flags & IW_ENCODE_DISABLED) {
+ if (*crypt)
+ prism2_crypt_delayed_deinit(local, crypt);
+ goto done;
+ }
+
+ if (*crypt != NULL && (*crypt)->ops != NULL &&
+ strcmp((*crypt)->ops->name, "WEP") != 0) {
+ /* changing to use WEP; deinit previously used algorithm */
+ prism2_crypt_delayed_deinit(local, crypt);
+ }
+
+ if (*crypt == NULL) {
+ struct ieee80211_crypt_data *new_crypt;
+
+ /* take WEP into use */
+ new_crypt = (struct ieee80211_crypt_data *)
+ kmalloc(sizeof(struct ieee80211_crypt_data),
+ GFP_KERNEL);
+ if (new_crypt == NULL)
+ return -ENOMEM;
+ memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
+ new_crypt->ops = ieee80211_get_crypto_ops("WEP");
+ if (!new_crypt->ops) {
+ request_module("ieee80211_crypt_wep");
+ new_crypt->ops = ieee80211_get_crypto_ops("WEP");
+ }
+ if (new_crypt->ops)
+ new_crypt->priv = new_crypt->ops->init(i);
+ if (!new_crypt->ops || !new_crypt->priv) {
+ kfree(new_crypt);
+ new_crypt = NULL;
+
+ printk(KERN_WARNING "%s: could not initialize WEP: "
+ "load module hostap_crypt_wep.o\n",
+ dev->name);
+ return -EOPNOTSUPP;
+ }
+ *crypt = new_crypt;
+ }
+
+ if (erq->length > 0) {
+ int len = erq->length <= 5 ? 5 : 13;
+ int first = 1, j;
+ if (len > erq->length)
+ memset(keybuf + erq->length, 0, len - erq->length);
+ (*crypt)->ops->set_key(keybuf, len, NULL, (*crypt)->priv);
+ for (j = 0; j < WEP_KEYS; j++) {
+ if (j != i && local->crypt[j]) {
+ first = 0;
+ break;
+ }
+ }
+ if (first)
+ local->tx_keyidx = i;
+ } else {
+ /* No key data - just set the default TX key index */
+ local->tx_keyidx = i;
+ }
+
+ done:
+ local->open_wep = erq->flags & IW_ENCODE_OPEN;
+
+ if (hostap_set_encryption(local)) {
+ printk(KERN_DEBUG "%s: set_encryption failed\n", dev->name);
+ return -EINVAL;
+ }
+
+ /* Do not reset port0 if card is in Managed mode since resetting will
+ * generate new IEEE 802.11 authentication which may end up in looping
+ * with IEEE 802.1X. Prism2 documentation seem to require port reset
+ * after WEP configuration. However, keys are apparently changed at
+ * least in Managed mode. */
+ if (local->iw_mode != IW_MODE_INFRA && local->func->reset_port(dev)) {
+ printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int prism2_ioctl_giwencode(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq, char *key)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int i, len;
+ u16 val;
+ struct ieee80211_crypt_data *crypt;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ i = erq->flags & IW_ENCODE_INDEX;
+ if (i < 1 || i > 4)
+ i = local->tx_keyidx;
+ else
+ i--;
+ if (i < 0 || i >= WEP_KEYS)
+ return -EINVAL;
+
+ crypt = local->crypt[i];
+ erq->flags = i + 1;
+
+ if (crypt == NULL || crypt->ops == NULL) {
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ return 0;
+ }
+
+ if (strcmp(crypt->ops->name, "WEP") != 0) {
+ /* only WEP is supported with wireless extensions, so just
+ * report that encryption is used */
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_ENABLED;
+ return 0;
+ }
+
+ /* Reads from HFA384X_RID_CNFDEFAULTKEY* return bogus values, so show
+ * the keys from driver buffer */
+ len = crypt->ops->get_key(key, WEP_KEY_LEN, NULL, crypt->priv);
+ erq->length = (len >= 0 ? len : 0);
+
+ if (local->func->get_rid(dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, 1) < 0)
+ {
+ printk("CNFWEPFLAGS reading failed\n");
+ return -EOPNOTSUPP;
+ }
+ le16_to_cpus(&val);
+ if (val & HFA384X_WEPFLAGS_PRIVACYINVOKED)
+ erq->flags |= IW_ENCODE_ENABLED;
+ else
+ erq->flags |= IW_ENCODE_DISABLED;
+ if (val & HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED)
+ erq->flags |= IW_ENCODE_RESTRICTED;
+ else
+ erq->flags |= IW_ENCODE_OPEN;
+
+ return 0;
+}
+
+
+static int hostap_set_rate(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int ret, basic_rates;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ basic_rates = local->basic_rates & local->tx_rate_control;
+ if (!basic_rates || basic_rates != local->basic_rates) {
+ printk(KERN_INFO "%s: updating basic rate set automatically "
+ "to match with the new supported rate set\n",
+ dev->name);
+ if (!basic_rates)
+ basic_rates = local->tx_rate_control;
+
+ local->basic_rates = basic_rates;
+ if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES,
+ basic_rates))
+ printk(KERN_WARNING "%s: failed to set "
+ "cnfBasicRates\n", dev->name);
+ }
+
+ ret = (hostap_set_word(dev, HFA384X_RID_TXRATECONTROL,
+ local->tx_rate_control) ||
+ hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES,
+ local->tx_rate_control) ||
+ local->func->reset_port(dev));
+
+ if (ret) {
+ printk(KERN_WARNING "%s: TXRateControl/cnfSupportedRates "
+ "setting to 0x%x failed\n",
+ dev->name, local->tx_rate_control);
+ }
+
+ /* Update TX rate configuration for all STAs based on new operational
+ * rate set. */
+ hostap_update_rates(local);
+
+ return ret;
+}
+
+
+static int prism2_ioctl_siwrate(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (rrq->fixed) {
+ switch (rrq->value) {
+ case 11000000:
+ local->tx_rate_control = HFA384X_RATES_11MBPS;
+ break;
+ case 5500000:
+ local->tx_rate_control = HFA384X_RATES_5MBPS;
+ break;
+ case 2000000:
+ local->tx_rate_control = HFA384X_RATES_2MBPS;
+ break;
+ case 1000000:
+ local->tx_rate_control = HFA384X_RATES_1MBPS;
+ break;
+ default:
+ local->tx_rate_control = HFA384X_RATES_1MBPS |
+ HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS |
+ HFA384X_RATES_11MBPS;
+ break;
+ }
+ } else {
+ switch (rrq->value) {
+ case 11000000:
+ local->tx_rate_control = HFA384X_RATES_1MBPS |
+ HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS |
+ HFA384X_RATES_11MBPS;
+ break;
+ case 5500000:
+ local->tx_rate_control = HFA384X_RATES_1MBPS |
+ HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS;
+ break;
+ case 2000000:
+ local->tx_rate_control = HFA384X_RATES_1MBPS |
+ HFA384X_RATES_2MBPS;
+ break;
+ case 1000000:
+ local->tx_rate_control = HFA384X_RATES_1MBPS;
+ break;
+ default:
+ local->tx_rate_control = HFA384X_RATES_1MBPS |
+ HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS |
+ HFA384X_RATES_11MBPS;
+ break;
+ }
+ }
+
+ return hostap_set_rate(dev);
+}
+
+
+static int prism2_ioctl_giwrate(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+ u16 val;
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int ret = 0;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->func->get_rid(dev, HFA384X_RID_TXRATECONTROL, &val, 2, 1) <
+ 0)
+ return -EINVAL;
+
+ if ((val & 0x1) && (val > 1))
+ rrq->fixed = 0;
+ else
+ rrq->fixed = 1;
+
+ if (local->iw_mode == IW_MODE_MASTER && local->ap != NULL &&
+ !local->fw_tx_rate_control) {
+ /* HFA384X_RID_CURRENTTXRATE seems to always be 2 Mbps in
+ * Host AP mode, so use the recorded TX rate of the last sent
+ * frame */
+ rrq->value = local->ap->last_tx_rate > 0 ?
+ local->ap->last_tx_rate * 100000 : 11000000;
+ return 0;
+ }
+
+ if (local->func->get_rid(dev, HFA384X_RID_CURRENTTXRATE, &val, 2, 1) <
+ 0)
+ return -EINVAL;
+
+ switch (val) {
+ case HFA384X_RATES_1MBPS:
+ rrq->value = 1000000;
+ break;
+ case HFA384X_RATES_2MBPS:
+ rrq->value = 2000000;
+ break;
+ case HFA384X_RATES_5MBPS:
+ rrq->value = 5500000;
+ break;
+ case HFA384X_RATES_11MBPS:
+ rrq->value = 11000000;
+ break;
+ default:
+ /* should not happen */
+ rrq->value = 11000000;
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+
+static int prism2_ioctl_siwsens(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *sens, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ /* Set the desired AP density */
+ if (sens->value < 1 || sens->value > 3)
+ return -EINVAL;
+
+ if (hostap_set_word(dev, HFA384X_RID_CNFSYSTEMSCALE, sens->value) ||
+ local->func->reset_port(dev))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int prism2_ioctl_giwsens(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *sens, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 val;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ /* Get the current AP density */
+ if (local->func->get_rid(dev, HFA384X_RID_CNFSYSTEMSCALE, &val, 2, 1) <
+ 0)
+ return -EINVAL;
+
+ sens->value = __le16_to_cpu(val);
+ sens->fixed = 1;
+
+ return 0;
+}
+
+
+/* Deprecated in new wireless extension API */
+static int prism2_ioctl_giwaplist(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct sockaddr *addr;
+ struct iw_quality *qual;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->iw_mode != IW_MODE_MASTER) {
+ printk(KERN_DEBUG "SIOCGIWAPLIST is currently only supported "
+ "in Host AP mode\n");
+ data->length = 0;
+ return -EOPNOTSUPP;
+ }
+
+ addr = kmalloc(sizeof(struct sockaddr) * IW_MAX_AP, GFP_KERNEL);
+ qual = kmalloc(sizeof(struct iw_quality) * IW_MAX_AP, GFP_KERNEL);
+ if (addr == NULL || qual == NULL) {
+ kfree(addr);
+ kfree(qual);
+ data->length = 0;
+ return -ENOMEM;
+ }
+
+ data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1);
+
+ memcpy(extra, &addr, sizeof(struct sockaddr) * data->length);
+ data->flags = 1; /* has quality information */
+ memcpy(extra + sizeof(struct sockaddr) * data->length, &qual,
+ sizeof(struct iw_quality) * data->length);
+
+ kfree(addr);
+ kfree(qual);
+
+ return 0;
+}
+
+
+static int prism2_ioctl_siwrts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 val;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (rts->disabled)
+ val = __constant_cpu_to_le16(2347);
+ else if (rts->value < 0 || rts->value > 2347)
+ return -EINVAL;
+ else
+ val = __cpu_to_le16(rts->value);
+
+ if (local->func->set_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2) ||
+ local->func->reset_port(dev))
+ return -EINVAL;
+
+ local->rts_threshold = rts->value;
+
+ return 0;
+}
+
+static int prism2_ioctl_giwrts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 val;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->func->get_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2, 1) <
+ 0)
+ return -EINVAL;
+
+ rts->value = __le16_to_cpu(val);
+ rts->disabled = (rts->value == 2347);
+ rts->fixed = 1;
+
+ return 0;
+}
+
+
+static int prism2_ioctl_siwfrag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 val;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (rts->disabled)
+ val = __constant_cpu_to_le16(2346);
+ else if (rts->value < 256 || rts->value > 2346)
+ return -EINVAL;
+ else
+ val = __cpu_to_le16(rts->value & ~0x1); /* even numbers only */
+
+ local->fragm_threshold = rts->value & ~0x1;
+ if (local->func->set_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, &val,
+ 2)
+ || local->func->reset_port(dev))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int prism2_ioctl_giwfrag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 val;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->func->get_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD,
+ &val, 2, 1) < 0)
+ return -EINVAL;
+
+ rts->value = __le16_to_cpu(val);
+ rts->disabled = (rts->value == 2346);
+ rts->fixed = 1;
+
+ return 0;
+}
+
+
+#ifndef PRISM2_NO_STATION_MODES
+static int hostap_join_ap(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct hfa384x_join_request req;
+ unsigned long flags;
+ int i;
+ struct hfa384x_hostscan_result *entry;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ memcpy(req.bssid, local->preferred_ap, ETH_ALEN);
+ req.channel = 0;
+
+ spin_lock_irqsave(&local->lock, flags);
+ for (i = 0; i < local->last_scan_results_count; i++) {
+ if (!local->last_scan_results)
+ break;
+ entry = &local->last_scan_results[i];
+ if (memcmp(local->preferred_ap, entry->bssid, ETH_ALEN) == 0) {
+ req.channel = entry->chid;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&local->lock, flags);
+
+ if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req,
+ sizeof(req))) {
+ printk(KERN_DEBUG "%s: JoinRequest " MACSTR
+ " failed\n",
+ dev->name, MAC2STR(local->preferred_ap));
+ return -1;
+ }
+
+ printk(KERN_DEBUG "%s: Trying to join BSSID " MACSTR "\n",
+ dev->name, MAC2STR(local->preferred_ap));
+
+ return 0;
+}
+#endif /* PRISM2_NO_STATION_MODES */
+
+
+static int prism2_ioctl_siwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+#ifdef PRISM2_NO_STATION_MODES
+ return -EOPNOTSUPP;
+#else /* PRISM2_NO_STATION_MODES */
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ memcpy(local->preferred_ap, &ap_addr->sa_data, ETH_ALEN);
+
+ if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA) {
+ struct hfa384x_scan_request scan_req;
+ memset(&scan_req, 0, sizeof(scan_req));
+ scan_req.channel_list = __constant_cpu_to_le16(0x3fff);
+ scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS);
+ if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST,
+ &scan_req, sizeof(scan_req))) {
+ printk(KERN_DEBUG "%s: ScanResults request failed - "
+ "preferred AP delayed to next unsolicited "
+ "scan\n", dev->name);
+ }
+ } else if (local->host_roaming == 2 &&
+ local->iw_mode == IW_MODE_INFRA) {
+ if (hostap_join_ap(dev))
+ return -EINVAL;
+ } else {
+ printk(KERN_DEBUG "%s: Preferred AP (SIOCSIWAP) is used only "
+ "in Managed mode when host_roaming is enabled\n",
+ dev->name);
+ }
+
+ return 0;
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+static int prism2_ioctl_giwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ ap_addr->sa_family = ARPHRD_ETHER;
+ switch (iface->type) {
+ case HOSTAP_INTERFACE_AP:
+ memcpy(&ap_addr->sa_data, dev->dev_addr, ETH_ALEN);
+ break;
+ case HOSTAP_INTERFACE_STA:
+ memcpy(&ap_addr->sa_data, local->assoc_ap_addr, ETH_ALEN);
+ break;
+ case HOSTAP_INTERFACE_WDS:
+ memcpy(&ap_addr->sa_data, iface->u.wds.remote_addr, ETH_ALEN);
+ break;
+ default:
+ if (local->func->get_rid(dev, HFA384X_RID_CURRENTBSSID,
+ &ap_addr->sa_data, ETH_ALEN, 1) < 0)
+ return -EOPNOTSUPP;
+
+ /* local->bssid is also updated in LinkStatus handler when in
+ * station mode */
+ memcpy(local->bssid, &ap_addr->sa_data, ETH_ALEN);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int prism2_ioctl_siwnickn(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *nickname)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ memset(local->name, 0, sizeof(local->name));
+ memcpy(local->name, nickname, data->length);
+ local->name_set = 1;
+
+ if (hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, local->name) ||
+ local->func->reset_port(dev))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int prism2_ioctl_giwnickn(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *nickname)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int len;
+ char name[MAX_NAME_LEN + 3];
+ u16 val;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ len = local->func->get_rid(dev, HFA384X_RID_CNFOWNNAME,
+ &name, MAX_NAME_LEN + 2, 0);
+ val = __le16_to_cpu(*(u16 *) name);
+ if (len > MAX_NAME_LEN + 2 || len < 0 || val > MAX_NAME_LEN)
+ return -EOPNOTSUPP;
+
+ name[val + 2] = '\0';
+ data->length = val + 1;
+ memcpy(nickname, name + 2, val + 1);
+
+ return 0;
+}
+
+
+static int prism2_ioctl_siwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ /* freq => chan. */
+ if (freq->e == 1 &&
+ freq->m / 100000 >= freq_list[0] &&
+ freq->m / 100000 <= freq_list[FREQ_COUNT - 1]) {
+ int ch;
+ int fr = freq->m / 100000;
+ for (ch = 0; ch < FREQ_COUNT; ch++) {
+ if (fr == freq_list[ch]) {
+ freq->e = 0;
+ freq->m = ch + 1;
+ break;
+ }
+ }
+ }
+
+ if (freq->e != 0 || freq->m < 1 || freq->m > FREQ_COUNT ||
+ !(local->channel_mask & (1 << (freq->m - 1))))
+ return -EINVAL;
+
+ local->channel = freq->m; /* channel is used in prism2_setup_rids() */
+ if (hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel) ||
+ local->func->reset_port(dev))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int prism2_ioctl_giwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 val;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->func->get_rid(dev, HFA384X_RID_CURRENTCHANNEL, &val, 2, 1) <
+ 0)
+ return -EINVAL;
+
+ le16_to_cpus(&val);
+ if (val < 1 || val > FREQ_COUNT)
+ return -EINVAL;
+
+ freq->m = freq_list[val - 1] * 100000;
+ freq->e = 1;
+
+ return 0;
+}
+
+
+static void hostap_monitor_set_type(local_info_t *local)
+{
+ struct net_device *dev = local->ddev;
+
+ if (dev == NULL)
+ return;
+
+ if (local->monitor_type == PRISM2_MONITOR_PRISM ||
+ local->monitor_type == PRISM2_MONITOR_CAPHDR) {
+ dev->type = ARPHRD_IEEE80211_PRISM;
+ dev->hard_header_parse =
+ hostap_80211_prism_header_parse;
+ } else {
+ dev->type = ARPHRD_IEEE80211;
+ dev->hard_header_parse = hostap_80211_header_parse;
+ }
+}
+
+
+static int prism2_ioctl_siwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (iface->type == HOSTAP_INTERFACE_WDS)
+ return -EOPNOTSUPP;
+
+ if (data->flags == 0)
+ ssid[0] = '\0'; /* ANY */
+
+ if (local->iw_mode == IW_MODE_MASTER && ssid[0] == '\0') {
+ /* Setting SSID to empty string seems to kill the card in
+ * Host AP mode */
+ printk(KERN_DEBUG "%s: Host AP mode does not support "
+ "'Any' essid\n", dev->name);
+ return -EINVAL;
+ }
+
+ memcpy(local->essid, ssid, data->length);
+ local->essid[data->length] = '\0';
+
+ if ((!local->fw_ap &&
+ hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, local->essid))
+ || hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, local->essid) ||
+ local->func->reset_port(dev))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int prism2_ioctl_giwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *essid)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 val;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (iface->type == HOSTAP_INTERFACE_WDS)
+ return -EOPNOTSUPP;
+
+ data->flags = 1; /* active */
+ if (local->iw_mode == IW_MODE_MASTER) {
+ data->length = strlen(local->essid);
+ memcpy(essid, local->essid, IW_ESSID_MAX_SIZE);
+ } else {
+ int len;
+ char ssid[MAX_SSID_LEN + 2];
+ memset(ssid, 0, sizeof(ssid));
+ len = local->func->get_rid(dev, HFA384X_RID_CURRENTSSID,
+ &ssid, MAX_SSID_LEN + 2, 0);
+ val = __le16_to_cpu(*(u16 *) ssid);
+ if (len > MAX_SSID_LEN + 2 || len < 0 || val > MAX_SSID_LEN) {
+ return -EOPNOTSUPP;
+ }
+ data->length = val;
+ memcpy(essid, ssid + 2, IW_ESSID_MAX_SIZE);
+ }
+
+ return 0;
+}
+
+
+static int prism2_ioctl_giwrange(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct iw_range *range = (struct iw_range *) extra;
+ u8 rates[10];
+ u16 val;
+ int i, len, over2;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ data->length = sizeof(struct iw_range);
+ memset(range, 0, sizeof(struct iw_range));
+
+ /* TODO: could fill num_txpower and txpower array with
+ * something; however, there are 128 different values.. */
+
+ range->txpower_capa = IW_TXPOW_DBM;
+
+ if (local->iw_mode == IW_MODE_INFRA || local->iw_mode == IW_MODE_ADHOC)
+ {
+ range->min_pmp = 1 * 1024;
+ range->max_pmp = 65535 * 1024;
+ range->min_pmt = 1 * 1024;
+ range->max_pmt = 1000 * 1024;
+ range->pmp_flags = IW_POWER_PERIOD;
+ range->pmt_flags = IW_POWER_TIMEOUT;
+ range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT |
+ IW_POWER_UNICAST_R | IW_POWER_ALL_R;
+ }
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 18;
+
+ range->retry_capa = IW_RETRY_LIMIT;
+ range->retry_flags = IW_RETRY_LIMIT;
+ range->min_retry = 0;
+ range->max_retry = 255;
+
+ range->num_channels = FREQ_COUNT;
+
+ val = 0;
+ for (i = 0; i < FREQ_COUNT; i++) {
+ if (local->channel_mask & (1 << i)) {
+ range->freq[val].i = i + 1;
+ range->freq[val].m = freq_list[i] * 100000;
+ range->freq[val].e = 1;
+ val++;
+ }
+ if (val == IW_MAX_FREQUENCIES)
+ break;
+ }
+ range->num_frequency = val;
+
+ if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) {
+ range->max_qual.qual = 70; /* what is correct max? This was not
+ * documented exactly. At least
+ * 69 has been observed. */
+ range->max_qual.level = 0; /* dB */
+ range->max_qual.noise = 0; /* dB */
+
+ /* What would be suitable values for "average/typical" qual? */
+ range->avg_qual.qual = 20;
+ range->avg_qual.level = -60;
+ range->avg_qual.noise = -95;
+ } else {
+ range->max_qual.qual = 92; /* 0 .. 92 */
+ range->max_qual.level = 154; /* 27 .. 154 */
+ range->max_qual.noise = 154; /* 27 .. 154 */
+ }
+ range->sensitivity = 3;
+
+ range->max_encoding_tokens = WEP_KEYS;
+ range->num_encoding_sizes = 2;
+ range->encoding_size[0] = 5;
+ range->encoding_size[1] = 13;
+
+ over2 = 0;
+ len = prism2_get_datarates(dev, rates);
+ range->num_bitrates = 0;
+ for (i = 0; i < len; i++) {
+ if (range->num_bitrates < IW_MAX_BITRATES) {
+ range->bitrate[range->num_bitrates] =
+ rates[i] * 500000;
+ range->num_bitrates++;
+ }
+ if (rates[i] == 0x0b || rates[i] == 0x16)
+ over2 = 1;
+ }
+ /* estimated maximum TCP throughput values (bps) */
+ range->throughput = over2 ? 5500000 : 1500000;
+
+ range->min_rts = 0;
+ range->max_rts = 2347;
+ range->min_frag = 256;
+ range->max_frag = 2346;
+
+ /* Event capability (kernel + driver) */
+ range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+ IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
+ IW_EVENT_CAPA_MASK(SIOCGIWAP) |
+ IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
+ range->event_capa[1] = IW_EVENT_CAPA_K_1;
+ range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVTXDROP) |
+ IW_EVENT_CAPA_MASK(IWEVCUSTOM) |
+ IW_EVENT_CAPA_MASK(IWEVREGISTERED) |
+ IW_EVENT_CAPA_MASK(IWEVEXPIRED));
+
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+
+ return 0;
+}
+
+
+static int hostap_monitor_mode_enable(local_info_t *local)
+{
+ struct net_device *dev = local->dev;
+
+ printk(KERN_DEBUG "Enabling monitor mode\n");
+ hostap_monitor_set_type(local);
+
+ if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE,
+ HFA384X_PORTTYPE_PSEUDO_IBSS)) {
+ printk(KERN_DEBUG "Port type setting for monitor mode "
+ "failed\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* Host decrypt is needed to get the IV and ICV fields;
+ * however, monitor mode seems to remove WEP flag from frame
+ * control field */
+ if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS,
+ HFA384X_WEPFLAGS_HOSTENCRYPT |
+ HFA384X_WEPFLAGS_HOSTDECRYPT)) {
+ printk(KERN_DEBUG "WEP flags setting failed\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (local->func->reset_port(dev) ||
+ local->func->cmd(dev, HFA384X_CMDCODE_TEST |
+ (HFA384X_TEST_MONITOR << 8),
+ 0, NULL, NULL)) {
+ printk(KERN_DEBUG "Setting monitor mode failed\n");
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+
+static int hostap_monitor_mode_disable(local_info_t *local)
+{
+ struct net_device *dev = local->ddev;
+
+ if (dev == NULL)
+ return -1;
+
+ printk(KERN_DEBUG "%s: Disabling monitor mode\n", dev->name);
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_parse = local->saved_eth_header_parse;
+ if (local->func->cmd(dev, HFA384X_CMDCODE_TEST |
+ (HFA384X_TEST_STOP << 8),
+ 0, NULL, NULL))
+ return -1;
+ return hostap_set_encryption(local);
+}
+
+
+static int prism2_ioctl_siwmode(struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *mode, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int double_reset = 0;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (*mode != IW_MODE_ADHOC && *mode != IW_MODE_INFRA &&
+ *mode != IW_MODE_MASTER && *mode != IW_MODE_REPEAT &&
+ *mode != IW_MODE_MONITOR)
+ return -EOPNOTSUPP;
+
+#ifdef PRISM2_NO_STATION_MODES
+ if (*mode == IW_MODE_ADHOC || *mode == IW_MODE_INFRA)
+ return -EOPNOTSUPP;
+#endif /* PRISM2_NO_STATION_MODES */
+
+ if (*mode == local->iw_mode)
+ return 0;
+
+ if (*mode == IW_MODE_MASTER && local->essid[0] == '\0') {
+ printk(KERN_WARNING "%s: empty SSID not allowed in Master "
+ "mode\n", dev->name);
+ return -EINVAL;
+ }
+
+ if (local->iw_mode == IW_MODE_MONITOR)
+ hostap_monitor_mode_disable(local);
+
+ if ((local->iw_mode == IW_MODE_ADHOC ||
+ local->iw_mode == IW_MODE_MONITOR) && *mode == IW_MODE_MASTER) {
+ /* There seems to be a firmware bug in at least STA f/w v1.5.6
+ * that leaves beacon frames to use IBSS type when moving from
+ * IBSS to Host AP mode. Doing double Port0 reset seems to be
+ * enough to workaround this. */
+ double_reset = 1;
+ }
+
+ printk(KERN_DEBUG "prism2: %s: operating mode changed "
+ "%d -> %d\n", dev->name, local->iw_mode, *mode);
+ local->iw_mode = *mode;
+
+ if (local->iw_mode == IW_MODE_MONITOR)
+ hostap_monitor_mode_enable(local);
+ else if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt &&
+ !local->fw_encrypt_ok) {
+ printk(KERN_DEBUG "%s: defaulting to host-based encryption as "
+ "a workaround for firmware bug in Host AP mode WEP\n",
+ dev->name);
+ local->host_encrypt = 1;
+ }
+
+ if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE,
+ hostap_get_porttype(local)))
+ return -EOPNOTSUPP;
+
+ if (local->func->reset_port(dev))
+ return -EINVAL;
+ if (double_reset && local->func->reset_port(dev))
+ return -EINVAL;
+
+ if (local->iw_mode != IW_MODE_INFRA && local->iw_mode != IW_MODE_ADHOC)
+ {
+ /* netif_carrier is used only in client modes for now, so make
+ * sure carrier is on when moving to non-client modes. */
+ netif_carrier_on(local->dev);
+ netif_carrier_on(local->ddev);
+ }
+ return 0;
+}
+
+
+static int prism2_ioctl_giwmode(struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *mode, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ switch (iface->type) {
+ case HOSTAP_INTERFACE_STA:
+ *mode = IW_MODE_INFRA;
+ break;
+ case HOSTAP_INTERFACE_WDS:
+ *mode = IW_MODE_REPEAT;
+ break;
+ default:
+ *mode = local->iw_mode;
+ break;
+ }
+ return 0;
+}
+
+
+static int prism2_ioctl_siwpower(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *wrq, char *extra)
+{
+#ifdef PRISM2_NO_STATION_MODES
+ return -EOPNOTSUPP;
+#else /* PRISM2_NO_STATION_MODES */
+ int ret = 0;
+
+ if (wrq->disabled)
+ return hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 0);
+
+ switch (wrq->flags & IW_POWER_MODE) {
+ case IW_POWER_UNICAST_R:
+ ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 0);
+ if (ret)
+ return ret;
+ ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1);
+ if (ret)
+ return ret;
+ break;
+ case IW_POWER_ALL_R:
+ ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 1);
+ if (ret)
+ return ret;
+ ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1);
+ if (ret)
+ return ret;
+ break;
+ case IW_POWER_ON:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (wrq->flags & IW_POWER_TIMEOUT) {
+ ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1);
+ if (ret)
+ return ret;
+ ret = hostap_set_word(dev, HFA384X_RID_CNFPMHOLDOVERDURATION,
+ wrq->value / 1024);
+ if (ret)
+ return ret;
+ }
+ if (wrq->flags & IW_POWER_PERIOD) {
+ ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1);
+ if (ret)
+ return ret;
+ ret = hostap_set_word(dev, HFA384X_RID_CNFMAXSLEEPDURATION,
+ wrq->value / 1024);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+
+static int prism2_ioctl_giwpower(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+#ifdef PRISM2_NO_STATION_MODES
+ return -EOPNOTSUPP;
+#else /* PRISM2_NO_STATION_MODES */
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 enable, mcast;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->func->get_rid(dev, HFA384X_RID_CNFPMENABLED, &enable, 2, 1)
+ < 0)
+ return -EINVAL;
+
+ if (!__le16_to_cpu(enable)) {
+ rrq->disabled = 1;
+ return 0;
+ }
+
+ rrq->disabled = 0;
+
+ if ((rrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+ u16 timeout;
+ if (local->func->get_rid(dev,
+ HFA384X_RID_CNFPMHOLDOVERDURATION,
+ &timeout, 2, 1) < 0)
+ return -EINVAL;
+
+ rrq->flags = IW_POWER_TIMEOUT;
+ rrq->value = __le16_to_cpu(timeout) * 1024;
+ } else {
+ u16 period;
+ if (local->func->get_rid(dev, HFA384X_RID_CNFMAXSLEEPDURATION,
+ &period, 2, 1) < 0)
+ return -EINVAL;
+
+ rrq->flags = IW_POWER_PERIOD;
+ rrq->value = __le16_to_cpu(period) * 1024;
+ }
+
+ if (local->func->get_rid(dev, HFA384X_RID_CNFMULTICASTRECEIVE, &mcast,
+ 2, 1) < 0)
+ return -EINVAL;
+
+ if (__le16_to_cpu(mcast))
+ rrq->flags |= IW_POWER_ALL_R;
+ else
+ rrq->flags |= IW_POWER_UNICAST_R;
+
+ return 0;
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+
+static int prism2_ioctl_siwretry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (rrq->disabled)
+ return -EINVAL;
+
+ /* setting retry limits is not supported with the current station
+ * firmware code; simulate this with alternative retry count for now */
+ if (rrq->flags == IW_RETRY_LIMIT) {
+ if (rrq->value < 0) {
+ /* disable manual retry count setting and use firmware
+ * defaults */
+ local->manual_retry_count = -1;
+ local->tx_control &= ~HFA384X_TX_CTRL_ALT_RTRY;
+ } else {
+ if (hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT,
+ rrq->value)) {
+ printk(KERN_DEBUG "%s: Alternate retry count "
+ "setting to %d failed\n",
+ dev->name, rrq->value);
+ return -EOPNOTSUPP;
+ }
+
+ local->manual_retry_count = rrq->value;
+ local->tx_control |= HFA384X_TX_CTRL_ALT_RTRY;
+ }
+ return 0;
+ }
+
+ return -EOPNOTSUPP;
+
+#if 0
+ /* what could be done, if firmware would support this.. */
+
+ if (rrq->flags & IW_RETRY_LIMIT) {
+ if (rrq->flags & IW_RETRY_MAX)
+ HFA384X_RID_LONGRETRYLIMIT = rrq->value;
+ else if (rrq->flags & IW_RETRY_MIN)
+ HFA384X_RID_SHORTRETRYLIMIT = rrq->value;
+ else {
+ HFA384X_RID_LONGRETRYLIMIT = rrq->value;
+ HFA384X_RID_SHORTRETRYLIMIT = rrq->value;
+ }
+
+ }
+
+ if (rrq->flags & IW_RETRY_LIFETIME) {
+ HFA384X_RID_MAXTRANSMITLIFETIME = rrq->value / 1024;
+ }
+
+ return 0;
+#endif /* 0 */
+}
+
+static int prism2_ioctl_giwretry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 shortretry, longretry, lifetime, altretry;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->func->get_rid(dev, HFA384X_RID_SHORTRETRYLIMIT, &shortretry,
+ 2, 1) < 0 ||
+ local->func->get_rid(dev, HFA384X_RID_LONGRETRYLIMIT, &longretry,
+ 2, 1) < 0 ||
+ local->func->get_rid(dev, HFA384X_RID_MAXTRANSMITLIFETIME,
+ &lifetime, 2, 1) < 0)
+ return -EINVAL;
+
+ le16_to_cpus(&shortretry);
+ le16_to_cpus(&longretry);
+ le16_to_cpus(&lifetime);
+
+ rrq->disabled = 0;
+
+ if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
+ rrq->flags = IW_RETRY_LIFETIME;
+ rrq->value = lifetime * 1024;
+ } else {
+ if (local->manual_retry_count >= 0) {
+ rrq->flags = IW_RETRY_LIMIT;
+ if (local->func->get_rid(dev,
+ HFA384X_RID_CNFALTRETRYCOUNT,
+ &altretry, 2, 1) >= 0)
+ rrq->value = le16_to_cpu(altretry);
+ else
+ rrq->value = local->manual_retry_count;
+ } else if ((rrq->flags & IW_RETRY_MAX)) {
+ rrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ rrq->value = longretry;
+ } else {
+ rrq->flags = IW_RETRY_LIMIT;
+ rrq->value = shortretry;
+ if (shortretry != longretry)
+ rrq->flags |= IW_RETRY_MIN;
+ }
+ }
+ return 0;
+}
+
+
+/* Note! This TX power controlling is experimental and should not be used in
+ * production use. It just sets raw power register and does not use any kind of
+ * feedback information from the measured TX power (CR58). This is now
+ * commented out to make sure that it is not used by accident. TX power
+ * configuration will be enabled again after proper algorithm using feedback
+ * has been implemented. */
+
+#ifdef RAW_TXPOWER_SETTING
+/* Map HFA386x's CR31 to and from dBm with some sort of ad hoc mapping..
+ * This version assumes following mapping:
+ * CR31 is 7-bit value with -64 to +63 range.
+ * -64 is mapped into +20dBm and +63 into -43dBm.
+ * This is certainly not an exact mapping for every card, but at least
+ * increasing dBm value should correspond to increasing TX power.
+ */
+
+static int prism2_txpower_hfa386x_to_dBm(u16 val)
+{
+ signed char tmp;
+
+ if (val > 255)
+ val = 255;
+
+ tmp = val;
+ tmp >>= 2;
+
+ return -12 - tmp;
+}
+
+static u16 prism2_txpower_dBm_to_hfa386x(int val)
+{
+ signed char tmp;
+
+ if (val > 20)
+ return 128;
+ else if (val < -43)
+ return 127;
+
+ tmp = val;
+ tmp = -12 - tmp;
+ tmp <<= 2;
+
+ return (unsigned char) tmp;
+}
+#endif /* RAW_TXPOWER_SETTING */
+
+
+static int prism2_ioctl_siwtxpow(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+#ifdef RAW_TXPOWER_SETTING
+ char *tmp;
+#endif
+ u16 val;
+ int ret = 0;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (rrq->disabled) {
+ if (local->txpower_type != PRISM2_TXPOWER_OFF) {
+ val = 0xff; /* use all standby and sleep modes */
+ ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF,
+ HFA386X_CR_A_D_TEST_MODES2,
+ &val, NULL);
+ printk(KERN_DEBUG "%s: Turning radio off: %s\n",
+ dev->name, ret ? "failed" : "OK");
+ local->txpower_type = PRISM2_TXPOWER_OFF;
+ }
+ return (ret ? -EOPNOTSUPP : 0);
+ }
+
+ if (local->txpower_type == PRISM2_TXPOWER_OFF) {
+ val = 0; /* disable all standby and sleep modes */
+ ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF,
+ HFA386X_CR_A_D_TEST_MODES2, &val, NULL);
+ printk(KERN_DEBUG "%s: Turning radio on: %s\n",
+ dev->name, ret ? "failed" : "OK");
+ local->txpower_type = PRISM2_TXPOWER_UNKNOWN;
+ }
+
+#ifdef RAW_TXPOWER_SETTING
+ if (!rrq->fixed && local->txpower_type != PRISM2_TXPOWER_AUTO) {
+ printk(KERN_DEBUG "Setting ALC on\n");
+ val = HFA384X_TEST_CFG_BIT_ALC;
+ local->func->cmd(dev, HFA384X_CMDCODE_TEST |
+ (HFA384X_TEST_CFG_BITS << 8), 1, &val, NULL);
+ local->txpower_type = PRISM2_TXPOWER_AUTO;
+ return 0;
+ }
+
+ if (local->txpower_type != PRISM2_TXPOWER_FIXED) {
+ printk(KERN_DEBUG "Setting ALC off\n");
+ val = HFA384X_TEST_CFG_BIT_ALC;
+ local->func->cmd(dev, HFA384X_CMDCODE_TEST |
+ (HFA384X_TEST_CFG_BITS << 8), 0, &val, NULL);
+ local->txpower_type = PRISM2_TXPOWER_FIXED;
+ }
+
+ if (rrq->flags == IW_TXPOW_DBM)
+ tmp = "dBm";
+ else if (rrq->flags == IW_TXPOW_MWATT)
+ tmp = "mW";
+ else
+ tmp = "UNKNOWN";
+ printk(KERN_DEBUG "Setting TX power to %d %s\n", rrq->value, tmp);
+
+ if (rrq->flags != IW_TXPOW_DBM) {
+ printk("SIOCSIWTXPOW with mW is not supported; use dBm\n");
+ return -EOPNOTSUPP;
+ }
+
+ local->txpower = rrq->value;
+ val = prism2_txpower_dBm_to_hfa386x(local->txpower);
+ if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF,
+ HFA386X_CR_MANUAL_TX_POWER, &val, NULL))
+ ret = -EOPNOTSUPP;
+#else /* RAW_TXPOWER_SETTING */
+ if (rrq->fixed)
+ ret = -EOPNOTSUPP;
+#endif /* RAW_TXPOWER_SETTING */
+
+ return ret;
+}
+
+static int prism2_ioctl_giwtxpow(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+#ifdef RAW_TXPOWER_SETTING
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 resp0;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ rrq->flags = IW_TXPOW_DBM;
+ rrq->disabled = 0;
+ rrq->fixed = 0;
+
+ if (local->txpower_type == PRISM2_TXPOWER_AUTO) {
+ if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF,
+ HFA386X_CR_MANUAL_TX_POWER,
+ NULL, &resp0) == 0) {
+ rrq->value = prism2_txpower_hfa386x_to_dBm(resp0);
+ } else {
+ /* Could not get real txpower; guess 15 dBm */
+ rrq->value = 15;
+ }
+ } else if (local->txpower_type == PRISM2_TXPOWER_OFF) {
+ rrq->value = 0;
+ rrq->disabled = 1;
+ } else if (local->txpower_type == PRISM2_TXPOWER_FIXED) {
+ rrq->value = local->txpower;
+ rrq->fixed = 1;
+ } else {
+ printk("SIOCGIWTXPOW - unknown txpower_type=%d\n",
+ local->txpower_type);
+ }
+ return 0;
+#else /* RAW_TXPOWER_SETTING */
+ return -EOPNOTSUPP;
+#endif /* RAW_TXPOWER_SETTING */
+}
+
+
+#ifndef PRISM2_NO_STATION_MODES
+
+/* HostScan request works with and without host_roaming mode. In addition, it
+ * does not break current association. However, it requires newer station
+ * firmware version (>= 1.3.1) than scan request. */
+static int prism2_request_hostscan(struct net_device *dev,
+ u8 *ssid, u8 ssid_len)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct hfa384x_hostscan_request scan_req;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ memset(&scan_req, 0, sizeof(scan_req));
+ scan_req.channel_list = cpu_to_le16(local->channel_mask &
+ local->scan_channel_mask);
+ scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS);
+ if (ssid) {
+ if (ssid_len > 32)
+ return -EINVAL;
+ scan_req.target_ssid_len = cpu_to_le16(ssid_len);
+ memcpy(scan_req.target_ssid, ssid, ssid_len);
+ }
+
+ if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req,
+ sizeof(scan_req))) {
+ printk(KERN_DEBUG "%s: HOSTSCAN failed\n", dev->name);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static int prism2_request_scan(struct net_device *dev)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ struct hfa384x_scan_request scan_req;
+ int ret = 0;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ memset(&scan_req, 0, sizeof(scan_req));
+ scan_req.channel_list = cpu_to_le16(local->channel_mask &
+ local->scan_channel_mask);
+ scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS);
+
+ /* FIX:
+ * It seems to be enough to set roaming mode for a short moment to
+ * host-based and then setup scanrequest data and return the mode to
+ * firmware-based.
+ *
+ * Master mode would need to drop to Managed mode for a short while
+ * to make scanning work.. Or sweep through the different channels and
+ * use passive scan based on beacons. */
+
+ if (!local->host_roaming)
+ hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE,
+ HFA384X_ROAMING_HOST);
+
+ if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, &scan_req,
+ sizeof(scan_req))) {
+ printk(KERN_DEBUG "SCANREQUEST failed\n");
+ ret = -EINVAL;
+ }
+
+ if (!local->host_roaming)
+ hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE,
+ HFA384X_ROAMING_FIRMWARE);
+
+ return 0;
+}
+
+#else /* !PRISM2_NO_STATION_MODES */
+
+static inline int prism2_request_hostscan(struct net_device *dev,
+ u8 *ssid, u8 ssid_len)
+{
+ return -EOPNOTSUPP;
+}
+
+
+static inline int prism2_request_scan(struct net_device *dev)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif /* !PRISM2_NO_STATION_MODES */
+
+
+static int prism2_ioctl_siwscan(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int ret;
+ u8 *ssid = NULL, ssid_len = 0;
+ struct iw_scan_req *req = (struct iw_scan_req *) extra;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (data->length < sizeof(struct iw_scan_req))
+ req = NULL;
+
+ if (local->iw_mode == IW_MODE_MASTER) {
+ /* In master mode, we just return the results of our local
+ * tables, so we don't need to start anything...
+ * Jean II */
+ data->length = 0;
+ return 0;
+ }
+
+ if (!local->dev_enabled)
+ return -ENETDOWN;
+
+ if (req && data->flags & IW_SCAN_THIS_ESSID) {
+ ssid = req->essid;
+ ssid_len = req->essid_len;
+
+ if (ssid_len &&
+ ((local->iw_mode != IW_MODE_INFRA &&
+ local->iw_mode != IW_MODE_ADHOC) ||
+ (local->sta_fw_ver < PRISM2_FW_VER(1,3,1))))
+ return -EOPNOTSUPP;
+ }
+
+ if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1))
+ ret = prism2_request_hostscan(dev, ssid, ssid_len);
+ else
+ ret = prism2_request_scan(dev);
+
+ if (ret == 0)
+ local->scan_timestamp = jiffies;
+
+ /* Could inquire F101, F103 or wait for SIOCGIWSCAN and read RID */
+
+ return ret;
+}
+
+
+#ifndef PRISM2_NO_STATION_MODES
+static char * __prism2_translate_scan(local_info_t *local,
+ struct hfa384x_hostscan_result *scan,
+ struct hostap_bss_info *bss,
+ char *current_ev, char *end_buf)
+{
+ int i, chan;
+ struct iw_event iwe;
+ char *current_val;
+ u16 capabilities;
+ u8 *pos;
+ u8 *ssid, *bssid;
+ size_t ssid_len;
+ char *buf;
+
+ if (bss) {
+ ssid = bss->ssid;
+ ssid_len = bss->ssid_len;
+ bssid = bss->bssid;
+ } else {
+ ssid = scan->ssid;
+ ssid_len = le16_to_cpu(scan->ssid_len);
+ bssid = scan->bssid;
+ }
+ if (ssid_len > 32)
+ ssid_len = 32;
+
+ /* First entry *MUST* be the AP MAC address */
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, bssid, ETH_ALEN);
+ /* FIX:
+ * I do not know how this is possible, but iwe_stream_add_event
+ * seems to re-order memcpy execution so that len is set only
+ * after copying.. Pre-setting len here "fixes" this, but real
+ * problems should be solved (after which these iwe.len
+ * settings could be removed from this function). */
+ iwe.len = IW_EV_ADDR_LEN;
+ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+ IW_EV_ADDR_LEN);
+
+ /* Other entries will be displayed in the order we give them */
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.length = ssid_len;
+ iwe.u.data.flags = 1;
+ iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ssid);
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWMODE;
+ if (bss) {
+ capabilities = bss->capab_info;
+ } else {
+ capabilities = le16_to_cpu(scan->capability);
+ }
+ if (capabilities & (WLAN_CAPABILITY_ESS |
+ WLAN_CAPABILITY_IBSS)) {
+ if (capabilities & WLAN_CAPABILITY_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ iwe.len = IW_EV_UINT_LEN;
+ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+ IW_EV_UINT_LEN);
+ }
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWFREQ;
+ if (scan) {
+ chan = scan->chid;
+ } else if (bss) {
+ chan = bss->chan;
+ } else {
+ chan = 0;
+ }
+
+ if (chan > 0) {
+ iwe.u.freq.m = freq_list[le16_to_cpu(chan - 1)] * 100000;
+ iwe.u.freq.e = 1;
+ iwe.len = IW_EV_FREQ_LEN;
+ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+ IW_EV_FREQ_LEN);
+ }
+
+ if (scan) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVQUAL;
+ if (local->last_scan_type == PRISM2_HOSTSCAN) {
+ iwe.u.qual.level = le16_to_cpu(scan->sl);
+ iwe.u.qual.noise = le16_to_cpu(scan->anl);
+ } else {
+ iwe.u.qual.level =
+ HFA384X_LEVEL_TO_dBm(le16_to_cpu(scan->sl));
+ iwe.u.qual.noise =
+ HFA384X_LEVEL_TO_dBm(le16_to_cpu(scan->anl));
+ }
+ iwe.len = IW_EV_QUAL_LEN;
+ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+ IW_EV_QUAL_LEN);
+ }
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWENCODE;
+ if (capabilities & WLAN_CAPABILITY_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, "");
+
+ /* TODO: add SuppRates into BSS table */
+ if (scan) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWRATE;
+ current_val = current_ev + IW_EV_LCP_LEN;
+ pos = scan->sup_rates;
+ for (i = 0; i < sizeof(scan->sup_rates); i++) {
+ if (pos[i] == 0)
+ break;
+ /* Bit rate given in 500 kb/s units (+ 0x80) */
+ iwe.u.bitrate.value = ((pos[i] & 0x7f) * 500000);
+ current_val = iwe_stream_add_value(
+ current_ev, current_val, end_buf, &iwe,
+ IW_EV_PARAM_LEN);
+ }
+ /* Check if we added any event */
+ if ((current_val - current_ev) > IW_EV_LCP_LEN)
+ current_ev = current_val;
+ }
+
+ /* TODO: add BeaconInt,resp_rate,atim into BSS table */
+ buf = kmalloc(MAX_WPA_IE_LEN * 2 + 30, GFP_KERNEL);
+ if (buf && scan) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, "bcn_int=%d", le16_to_cpu(scan->beacon_interval));
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+ buf);
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, "resp_rate=%d", le16_to_cpu(scan->rate));
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+ buf);
+
+ if (local->last_scan_type == PRISM2_HOSTSCAN &&
+ (capabilities & WLAN_CAPABILITY_IBSS)) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, "atim=%d", le16_to_cpu(scan->atim));
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(current_ev, end_buf,
+ &iwe, buf);
+ }
+ }
+ kfree(buf);
+
+ if (bss && bss->wpa_ie_len > 0 && bss->wpa_ie_len <= MAX_WPA_IE_LEN) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = bss->wpa_ie_len;
+ current_ev = iwe_stream_add_point(
+ current_ev, end_buf, &iwe, bss->wpa_ie);
+ }
+
+ if (bss && bss->rsn_ie_len > 0 && bss->rsn_ie_len <= MAX_WPA_IE_LEN) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = bss->rsn_ie_len;
+ current_ev = iwe_stream_add_point(
+ current_ev, end_buf, &iwe, bss->rsn_ie);
+ }
+
+ return current_ev;
+}
+
+
+/* Translate scan data returned from the card to a card independant
+ * format that the Wireless Tools will understand - Jean II */
+static inline int prism2_translate_scan(local_info_t *local,
+ char *buffer, int buflen)
+{
+ struct hfa384x_hostscan_result *scan;
+ int entry, hostscan;
+ char *current_ev = buffer;
+ char *end_buf = buffer + buflen;
+ struct list_head *ptr;
+
+ spin_lock_bh(&local->lock);
+
+ list_for_each(ptr, &local->bss_list) {
+ struct hostap_bss_info *bss;
+ bss = list_entry(ptr, struct hostap_bss_info, list);
+ bss->included = 0;
+ }
+
+ hostscan = local->last_scan_type == PRISM2_HOSTSCAN;
+ for (entry = 0; entry < local->last_scan_results_count; entry++) {
+ int found = 0;
+ scan = &local->last_scan_results[entry];
+
+ /* Report every SSID if the AP is using multiple SSIDs. If no
+ * BSS record is found (e.g., when WPA mode is disabled),
+ * report the AP once. */
+ list_for_each(ptr, &local->bss_list) {
+ struct hostap_bss_info *bss;
+ bss = list_entry(ptr, struct hostap_bss_info, list);
+ if (memcmp(bss->bssid, scan->bssid, ETH_ALEN) == 0) {
+ bss->included = 1;
+ current_ev = __prism2_translate_scan(
+ local, scan, bss, current_ev, end_buf);
+ found++;
+ }
+ }
+ if (!found) {
+ current_ev = __prism2_translate_scan(
+ local, scan, NULL, current_ev, end_buf);
+ }
+ /* Check if there is space for one more entry */
+ if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a bigger buffer */
+ spin_unlock_bh(&local->lock);
+ return -E2BIG;
+ }
+ }
+
+ /* Prism2 firmware has limits (32 at least in some versions) for number
+ * of BSSes in scan results. Extend this limit by using local BSS list.
+ */
+ list_for_each(ptr, &local->bss_list) {
+ struct hostap_bss_info *bss;
+ bss = list_entry(ptr, struct hostap_bss_info, list);
+ if (bss->included)
+ continue;
+ current_ev = __prism2_translate_scan(local, NULL, bss,
+ current_ev, end_buf);
+ /* Check if there is space for one more entry */
+ if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a bigger buffer */
+ spin_unlock_bh(&local->lock);
+ return -E2BIG;
+ }
+ }
+
+ spin_unlock_bh(&local->lock);
+
+ return current_ev - buffer;
+}
+#endif /* PRISM2_NO_STATION_MODES */
+
+
+static inline int prism2_ioctl_giwscan_sta(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+#ifdef PRISM2_NO_STATION_MODES
+ return -EOPNOTSUPP;
+#else /* PRISM2_NO_STATION_MODES */
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int res;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ /* Wait until the scan is finished. We can probably do better
+ * than that - Jean II */
+ if (local->scan_timestamp &&
+ time_before(jiffies, local->scan_timestamp + 3 * HZ)) {
+ /* Important note : we don't want to block the caller
+ * until results are ready for various reasons.
+ * First, managing wait queues is complex and racy
+ * (there may be multiple simultaneous callers).
+ * Second, we grab some rtnetlink lock before comming
+ * here (in dev_ioctl()).
+ * Third, the caller can wait on the Wireless Event
+ * - Jean II */
+ return -EAGAIN;
+ }
+ local->scan_timestamp = 0;
+
+ res = prism2_translate_scan(local, extra, data->length);
+
+ if (res >= 0) {
+ data->length = res;
+ return 0;
+ } else {
+ data->length = 0;
+ return res;
+ }
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+
+static int prism2_ioctl_giwscan(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int res;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->iw_mode == IW_MODE_MASTER) {
+ /* In MASTER mode, it doesn't make sense to go around
+ * scanning the frequencies and make the stations we serve
+ * wait when what the user is really interested about is the
+ * list of stations and access points we are talking to.
+ * So, just extract results from our cache...
+ * Jean II */
+
+ /* Translate to WE format */
+ res = prism2_ap_translate_scan(dev, extra);
+ if (res >= 0) {
+ printk(KERN_DEBUG "Scan result translation succeeded "
+ "(length=%d)\n", res);
+ data->length = res;
+ return 0;
+ } else {
+ printk(KERN_DEBUG
+ "Scan result translation failed (res=%d)\n",
+ res);
+ data->length = 0;
+ return res;
+ }
+ } else {
+ /* Station mode */
+ return prism2_ioctl_giwscan_sta(dev, info, data, extra);
+ }
+}
+
+
+static const struct iw_priv_args prism2_priv[] = {
+ { PRISM2_IOCTL_MONITOR,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor" },
+ { PRISM2_IOCTL_READMIF,
+ IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+ IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "readmif" },
+ { PRISM2_IOCTL_WRITEMIF,
+ IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2, 0, "writemif" },
+ { PRISM2_IOCTL_RESET,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "reset" },
+ { PRISM2_IOCTL_INQUIRE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inquire" },
+ { PRISM2_IOCTL_SET_RID_WORD,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_rid_word" },
+ { PRISM2_IOCTL_MACCMD,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maccmd" },
+ { PRISM2_IOCTL_WDS_ADD,
+ IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_add" },
+ { PRISM2_IOCTL_WDS_DEL,
+ IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_del" },
+ { PRISM2_IOCTL_ADDMAC,
+ IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "addmac" },
+ { PRISM2_IOCTL_DELMAC,
+ IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "delmac" },
+ { PRISM2_IOCTL_KICKMAC,
+ IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac" },
+ /* --- raw access to sub-ioctls --- */
+ { PRISM2_IOCTL_PRISM2_PARAM,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "prism2_param" },
+ { PRISM2_IOCTL_GET_PRISM2_PARAM,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprism2_param" },
+ /* --- sub-ioctls handlers --- */
+ { PRISM2_IOCTL_PRISM2_PARAM,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "" },
+ { PRISM2_IOCTL_GET_PRISM2_PARAM,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" },
+ /* --- sub-ioctls definitions --- */
+ { PRISM2_PARAM_TXRATECTRL,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "txratectrl" },
+ { PRISM2_PARAM_TXRATECTRL,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettxratectrl" },
+ { PRISM2_PARAM_BEACON_INT,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "beacon_int" },
+ { PRISM2_PARAM_BEACON_INT,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbeacon_int" },
+#ifndef PRISM2_NO_STATION_MODES
+ { PRISM2_PARAM_PSEUDO_IBSS,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pseudo_ibss" },
+ { PRISM2_PARAM_PSEUDO_IBSS,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getpseudo_ibss" },
+#endif /* PRISM2_NO_STATION_MODES */
+ { PRISM2_PARAM_ALC,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "alc" },
+ { PRISM2_PARAM_ALC,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getalc" },
+ { PRISM2_PARAM_DUMP,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dump" },
+ { PRISM2_PARAM_DUMP,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdump" },
+ { PRISM2_PARAM_OTHER_AP_POLICY,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "other_ap_policy" },
+ { PRISM2_PARAM_OTHER_AP_POLICY,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getother_ap_pol" },
+ { PRISM2_PARAM_AP_MAX_INACTIVITY,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_inactivity" },
+ { PRISM2_PARAM_AP_MAX_INACTIVITY,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_inactivi" },
+ { PRISM2_PARAM_AP_BRIDGE_PACKETS,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bridge_packets" },
+ { PRISM2_PARAM_AP_BRIDGE_PACKETS,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbridge_packe" },
+ { PRISM2_PARAM_DTIM_PERIOD,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dtim_period" },
+ { PRISM2_PARAM_DTIM_PERIOD,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdtim_period" },
+ { PRISM2_PARAM_AP_NULLFUNC_ACK,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "nullfunc_ack" },
+ { PRISM2_PARAM_AP_NULLFUNC_ACK,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getnullfunc_ack" },
+ { PRISM2_PARAM_MAX_WDS,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_wds" },
+ { PRISM2_PARAM_MAX_WDS,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_wds" },
+ { PRISM2_PARAM_AP_AUTOM_AP_WDS,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "autom_ap_wds" },
+ { PRISM2_PARAM_AP_AUTOM_AP_WDS,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getautom_ap_wds" },
+ { PRISM2_PARAM_AP_AUTH_ALGS,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_auth_algs" },
+ { PRISM2_PARAM_AP_AUTH_ALGS,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_auth_algs" },
+ { PRISM2_PARAM_MONITOR_ALLOW_FCSERR,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "allow_fcserr" },
+ { PRISM2_PARAM_MONITOR_ALLOW_FCSERR,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getallow_fcserr" },
+ { PRISM2_PARAM_HOST_ENCRYPT,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_encrypt" },
+ { PRISM2_PARAM_HOST_ENCRYPT,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_encrypt" },
+ { PRISM2_PARAM_HOST_DECRYPT,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_decrypt" },
+ { PRISM2_PARAM_HOST_DECRYPT,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_decrypt" },
+#ifndef PRISM2_NO_STATION_MODES
+ { PRISM2_PARAM_HOST_ROAMING,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_roaming" },
+ { PRISM2_PARAM_HOST_ROAMING,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_roaming" },
+#endif /* PRISM2_NO_STATION_MODES */
+ { PRISM2_PARAM_BCRX_STA_KEY,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bcrx_sta_key" },
+ { PRISM2_PARAM_BCRX_STA_KEY,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbcrx_sta_key" },
+ { PRISM2_PARAM_IEEE_802_1X,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ieee_802_1x" },
+ { PRISM2_PARAM_IEEE_802_1X,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getieee_802_1x" },
+ { PRISM2_PARAM_ANTSEL_TX,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_tx" },
+ { PRISM2_PARAM_ANTSEL_TX,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_tx" },
+ { PRISM2_PARAM_ANTSEL_RX,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_rx" },
+ { PRISM2_PARAM_ANTSEL_RX,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_rx" },
+ { PRISM2_PARAM_MONITOR_TYPE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor_type" },
+ { PRISM2_PARAM_MONITOR_TYPE,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmonitor_type" },
+ { PRISM2_PARAM_WDS_TYPE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wds_type" },
+ { PRISM2_PARAM_WDS_TYPE,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwds_type" },
+ { PRISM2_PARAM_HOSTSCAN,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostscan" },
+ { PRISM2_PARAM_HOSTSCAN,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostscan" },
+ { PRISM2_PARAM_AP_SCAN,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_scan" },
+ { PRISM2_PARAM_AP_SCAN,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_scan" },
+ { PRISM2_PARAM_ENH_SEC,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "enh_sec" },
+ { PRISM2_PARAM_ENH_SEC,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getenh_sec" },
+#ifdef PRISM2_IO_DEBUG
+ { PRISM2_PARAM_IO_DEBUG,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "io_debug" },
+ { PRISM2_PARAM_IO_DEBUG,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getio_debug" },
+#endif /* PRISM2_IO_DEBUG */
+ { PRISM2_PARAM_BASIC_RATES,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "basic_rates" },
+ { PRISM2_PARAM_BASIC_RATES,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbasic_rates" },
+ { PRISM2_PARAM_OPER_RATES,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "oper_rates" },
+ { PRISM2_PARAM_OPER_RATES,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getoper_rates" },
+ { PRISM2_PARAM_HOSTAPD,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd" },
+ { PRISM2_PARAM_HOSTAPD,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd" },
+ { PRISM2_PARAM_HOSTAPD_STA,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd_sta" },
+ { PRISM2_PARAM_HOSTAPD_STA,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd_sta" },
+ { PRISM2_PARAM_WPA,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wpa" },
+ { PRISM2_PARAM_WPA,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwpa" },
+ { PRISM2_PARAM_PRIVACY_INVOKED,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "privacy_invoked" },
+ { PRISM2_PARAM_PRIVACY_INVOKED,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprivacy_invo" },
+ { PRISM2_PARAM_TKIP_COUNTERMEASURES,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tkip_countermea" },
+ { PRISM2_PARAM_TKIP_COUNTERMEASURES,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettkip_counter" },
+ { PRISM2_PARAM_DROP_UNENCRYPTED,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "drop_unencrypte" },
+ { PRISM2_PARAM_DROP_UNENCRYPTED,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdrop_unencry" },
+ { PRISM2_PARAM_SCAN_CHANNEL_MASK,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "scan_channels" },
+ { PRISM2_PARAM_SCAN_CHANNEL_MASK,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getscan_channel" },
+};
+
+
+static int prism2_ioctl_priv_inquire(struct net_device *dev, int *i)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->func->cmd(dev, HFA384X_CMDCODE_INQUIRE, *i, NULL, NULL))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+
+static int prism2_ioctl_priv_prism2_param(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int *i = (int *) extra;
+ int param = *i;
+ int value = *(i + 1);
+ int ret = 0;
+ u16 val;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ switch (param) {
+ case PRISM2_PARAM_TXRATECTRL:
+ local->fw_tx_rate_control = value;
+ break;
+
+ case PRISM2_PARAM_BEACON_INT:
+ if (hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, value) ||
+ local->func->reset_port(dev))
+ ret = -EINVAL;
+ else
+ local->beacon_int = value;
+ break;
+
+#ifndef PRISM2_NO_STATION_MODES
+ case PRISM2_PARAM_PSEUDO_IBSS:
+ if (value == local->pseudo_adhoc)
+ break;
+
+ if (value != 0 && value != 1) {
+ ret = -EINVAL;
+ break;
+ }
+
+ printk(KERN_DEBUG "prism2: %s: pseudo IBSS change %d -> %d\n",
+ dev->name, local->pseudo_adhoc, value);
+ local->pseudo_adhoc = value;
+ if (local->iw_mode != IW_MODE_ADHOC)
+ break;
+
+ if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE,
+ hostap_get_porttype(local))) {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (local->func->reset_port(dev))
+ ret = -EINVAL;
+ break;
+#endif /* PRISM2_NO_STATION_MODES */
+
+ case PRISM2_PARAM_ALC:
+ printk(KERN_DEBUG "%s: %s ALC\n", dev->name,
+ value == 0 ? "Disabling" : "Enabling");
+ val = HFA384X_TEST_CFG_BIT_ALC;
+ local->func->cmd(dev, HFA384X_CMDCODE_TEST |
+ (HFA384X_TEST_CFG_BITS << 8),
+ value == 0 ? 0 : 1, &val, NULL);
+ break;
+
+ case PRISM2_PARAM_DUMP:
+ local->frame_dump = value;
+ break;
+
+ case PRISM2_PARAM_OTHER_AP_POLICY:
+ if (value < 0 || value > 3) {
+ ret = -EINVAL;
+ break;
+ }
+ if (local->ap != NULL)
+ local->ap->ap_policy = value;
+ break;
+
+ case PRISM2_PARAM_AP_MAX_INACTIVITY:
+ if (value < 0 || value > 7 * 24 * 60 * 60) {
+ ret = -EINVAL;
+ break;
+ }
+ if (local->ap != NULL)
+ local->ap->max_inactivity = value * HZ;
+ break;
+
+ case PRISM2_PARAM_AP_BRIDGE_PACKETS:
+ if (local->ap != NULL)
+ local->ap->bridge_packets = value;
+ break;
+
+ case PRISM2_PARAM_DTIM_PERIOD:
+ if (value < 0 || value > 65535) {
+ ret = -EINVAL;
+ break;
+ }
+ if (hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, value)
+ || local->func->reset_port(dev))
+ ret = -EINVAL;
+ else
+ local->dtim_period = value;
+ break;
+
+ case PRISM2_PARAM_AP_NULLFUNC_ACK:
+ if (local->ap != NULL)
+ local->ap->nullfunc_ack = value;
+ break;
+
+ case PRISM2_PARAM_MAX_WDS:
+ local->wds_max_connections = value;
+ break;
+
+ case PRISM2_PARAM_AP_AUTOM_AP_WDS:
+ if (local->ap != NULL) {
+ if (!local->ap->autom_ap_wds && value) {
+ /* add WDS link to all APs in STA table */
+ hostap_add_wds_links(local);
+ }
+ local->ap->autom_ap_wds = value;
+ }
+ break;
+
+ case PRISM2_PARAM_AP_AUTH_ALGS:
+ local->auth_algs = value;
+ if (hostap_set_auth_algs(local))
+ ret = -EINVAL;
+ break;
+
+ case PRISM2_PARAM_MONITOR_ALLOW_FCSERR:
+ local->monitor_allow_fcserr = value;
+ break;
+
+ case PRISM2_PARAM_HOST_ENCRYPT:
+ local->host_encrypt = value;
+ if (hostap_set_encryption(local) ||
+ local->func->reset_port(dev))
+ ret = -EINVAL;
+ break;
+
+ case PRISM2_PARAM_HOST_DECRYPT:
+ local->host_decrypt = value;
+ if (hostap_set_encryption(local) ||
+ local->func->reset_port(dev))
+ ret = -EINVAL;
+ break;
+
+#ifndef PRISM2_NO_STATION_MODES
+ case PRISM2_PARAM_HOST_ROAMING:
+ if (value < 0 || value > 2) {
+ ret = -EINVAL;
+ break;
+ }
+ local->host_roaming = value;
+ if (hostap_set_roaming(local) || local->func->reset_port(dev))
+ ret = -EINVAL;
+ break;
+#endif /* PRISM2_NO_STATION_MODES */
+
+ case PRISM2_PARAM_BCRX_STA_KEY:
+ local->bcrx_sta_key = value;
+ break;
+
+ case PRISM2_PARAM_IEEE_802_1X:
+ local->ieee_802_1x = value;
+ break;
+
+ case PRISM2_PARAM_ANTSEL_TX:
+ if (value < 0 || value > HOSTAP_ANTSEL_HIGH) {
+ ret = -EINVAL;
+ break;
+ }
+ local->antsel_tx = value;
+ hostap_set_antsel(local);
+ break;
+
+ case PRISM2_PARAM_ANTSEL_RX:
+ if (value < 0 || value > HOSTAP_ANTSEL_HIGH) {
+ ret = -EINVAL;
+ break;
+ }
+ local->antsel_rx = value;
+ hostap_set_antsel(local);
+ break;
+
+ case PRISM2_PARAM_MONITOR_TYPE:
+ if (value != PRISM2_MONITOR_80211 &&
+ value != PRISM2_MONITOR_CAPHDR &&
+ value != PRISM2_MONITOR_PRISM) {
+ ret = -EINVAL;
+ break;
+ }
+ local->monitor_type = value;
+ if (local->iw_mode == IW_MODE_MONITOR)
+ hostap_monitor_set_type(local);
+ break;
+
+ case PRISM2_PARAM_WDS_TYPE:
+ local->wds_type = value;
+ break;
+
+ case PRISM2_PARAM_HOSTSCAN:
+ {
+ struct hfa384x_hostscan_request scan_req;
+ u16 rate;
+
+ memset(&scan_req, 0, sizeof(scan_req));
+ scan_req.channel_list = __constant_cpu_to_le16(0x3fff);
+ switch (value) {
+ case 1: rate = HFA384X_RATES_1MBPS; break;
+ case 2: rate = HFA384X_RATES_2MBPS; break;
+ case 3: rate = HFA384X_RATES_5MBPS; break;
+ case 4: rate = HFA384X_RATES_11MBPS; break;
+ default: rate = HFA384X_RATES_1MBPS; break;
+ }
+ scan_req.txrate = cpu_to_le16(rate);
+ /* leave SSID empty to accept all SSIDs */
+
+ if (local->iw_mode == IW_MODE_MASTER) {
+ if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE,
+ HFA384X_PORTTYPE_BSS) ||
+ local->func->reset_port(dev))
+ printk(KERN_DEBUG "Leaving Host AP mode "
+ "for HostScan failed\n");
+ }
+
+ if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req,
+ sizeof(scan_req))) {
+ printk(KERN_DEBUG "HOSTSCAN failed\n");
+ ret = -EINVAL;
+ }
+ if (local->iw_mode == IW_MODE_MASTER) {
+ wait_queue_t __wait;
+ init_waitqueue_entry(&__wait, current);
+ add_wait_queue(&local->hostscan_wq, &__wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+ if (signal_pending(current))
+ ret = -EINTR;
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&local->hostscan_wq, &__wait);
+
+ if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE,
+ HFA384X_PORTTYPE_HOSTAP) ||
+ local->func->reset_port(dev))
+ printk(KERN_DEBUG "Returning to Host AP mode "
+ "after HostScan failed\n");
+ }
+ break;
+ }
+
+ case PRISM2_PARAM_AP_SCAN:
+ local->passive_scan_interval = value;
+ if (timer_pending(&local->passive_scan_timer))
+ del_timer(&local->passive_scan_timer);
+ if (value > 0) {
+ local->passive_scan_timer.expires = jiffies +
+ local->passive_scan_interval * HZ;
+ add_timer(&local->passive_scan_timer);
+ }
+ break;
+
+ case PRISM2_PARAM_ENH_SEC:
+ if (value < 0 || value > 3) {
+ ret = -EINVAL;
+ break;
+ }
+ local->enh_sec = value;
+ if (hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY,
+ local->enh_sec) ||
+ local->func->reset_port(dev)) {
+ printk(KERN_INFO "%s: cnfEnhSecurity requires STA f/w "
+ "1.6.3 or newer\n", dev->name);
+ ret = -EOPNOTSUPP;
+ }
+ break;
+
+#ifdef PRISM2_IO_DEBUG
+ case PRISM2_PARAM_IO_DEBUG:
+ local->io_debug_enabled = value;
+ break;
+#endif /* PRISM2_IO_DEBUG */
+
+ case PRISM2_PARAM_BASIC_RATES:
+ if ((value & local->tx_rate_control) != value || value == 0) {
+ printk(KERN_INFO "%s: invalid basic rate set - basic "
+ "rates must be in supported rate set\n",
+ dev->name);
+ ret = -EINVAL;
+ break;
+ }
+ local->basic_rates = value;
+ if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES,
+ local->basic_rates) ||
+ local->func->reset_port(dev))
+ ret = -EINVAL;
+ break;
+
+ case PRISM2_PARAM_OPER_RATES:
+ local->tx_rate_control = value;
+ if (hostap_set_rate(dev))
+ ret = -EINVAL;
+ break;
+
+ case PRISM2_PARAM_HOSTAPD:
+ ret = hostap_set_hostapd(local, value, 1);
+ break;
+
+ case PRISM2_PARAM_HOSTAPD_STA:
+ ret = hostap_set_hostapd_sta(local, value, 1);
+ break;
+
+ case PRISM2_PARAM_WPA:
+ local->wpa = value;
+ if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0))
+ ret = -EOPNOTSUPP;
+ else if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE,
+ value ? 1 : 0))
+ ret = -EINVAL;
+ break;
+
+ case PRISM2_PARAM_PRIVACY_INVOKED:
+ local->privacy_invoked = value;
+ if (hostap_set_encryption(local) ||
+ local->func->reset_port(dev))
+ ret = -EINVAL;
+ break;
+
+ case PRISM2_PARAM_TKIP_COUNTERMEASURES:
+ local->tkip_countermeasures = value;
+ break;
+
+ case PRISM2_PARAM_DROP_UNENCRYPTED:
+ local->drop_unencrypted = value;
+ break;
+
+ case PRISM2_PARAM_SCAN_CHANNEL_MASK:
+ local->scan_channel_mask = value;
+ break;
+
+ default:
+ printk(KERN_DEBUG "%s: prism2_param: unknown param %d\n",
+ dev->name, param);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+
+static int prism2_ioctl_priv_get_prism2_param(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int *param = (int *) extra;
+ int ret = 0;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ switch (*param) {
+ case PRISM2_PARAM_TXRATECTRL:
+ *param = local->fw_tx_rate_control;
+ break;
+
+ case PRISM2_PARAM_BEACON_INT:
+ *param = local->beacon_int;
+ break;
+
+ case PRISM2_PARAM_PSEUDO_IBSS:
+ *param = local->pseudo_adhoc;
+ break;
+
+ case PRISM2_PARAM_ALC:
+ ret = -EOPNOTSUPP; /* FIX */
+ break;
+
+ case PRISM2_PARAM_DUMP:
+ *param = local->frame_dump;
+ break;
+
+ case PRISM2_PARAM_OTHER_AP_POLICY:
+ if (local->ap != NULL)
+ *param = local->ap->ap_policy;
+ else
+ ret = -EOPNOTSUPP;
+ break;
+
+ case PRISM2_PARAM_AP_MAX_INACTIVITY:
+ if (local->ap != NULL)
+ *param = local->ap->max_inactivity / HZ;
+ else
+ ret = -EOPNOTSUPP;
+ break;
+
+ case PRISM2_PARAM_AP_BRIDGE_PACKETS:
+ if (local->ap != NULL)
+ *param = local->ap->bridge_packets;
+ else
+ ret = -EOPNOTSUPP;
+ break;
+
+ case PRISM2_PARAM_DTIM_PERIOD:
+ *param = local->dtim_period;
+ break;
+
+ case PRISM2_PARAM_AP_NULLFUNC_ACK:
+ if (local->ap != NULL)
+ *param = local->ap->nullfunc_ack;
+ else
+ ret = -EOPNOTSUPP;
+ break;
+
+ case PRISM2_PARAM_MAX_WDS:
+ *param = local->wds_max_connections;
+ break;
+
+ case PRISM2_PARAM_AP_AUTOM_AP_WDS:
+ if (local->ap != NULL)
+ *param = local->ap->autom_ap_wds;
+ else
+ ret = -EOPNOTSUPP;
+ break;
+
+ case PRISM2_PARAM_AP_AUTH_ALGS:
+ *param = local->auth_algs;
+ break;
+
+ case PRISM2_PARAM_MONITOR_ALLOW_FCSERR:
+ *param = local->monitor_allow_fcserr;
+ break;
+
+ case PRISM2_PARAM_HOST_ENCRYPT:
+ *param = local->host_encrypt;
+ break;
+
+ case PRISM2_PARAM_HOST_DECRYPT:
+ *param = local->host_decrypt;
+ break;
+
+ case PRISM2_PARAM_HOST_ROAMING:
+ *param = local->host_roaming;
+ break;
+
+ case PRISM2_PARAM_BCRX_STA_KEY:
+ *param = local->bcrx_sta_key;
+ break;
+
+ case PRISM2_PARAM_IEEE_802_1X:
+ *param = local->ieee_802_1x;
+ break;
+
+ case PRISM2_PARAM_ANTSEL_TX:
+ *param = local->antsel_tx;
+ break;
+
+ case PRISM2_PARAM_ANTSEL_RX:
+ *param = local->antsel_rx;
+ break;
+
+ case PRISM2_PARAM_MONITOR_TYPE:
+ *param = local->monitor_type;
+ break;
+
+ case PRISM2_PARAM_WDS_TYPE:
+ *param = local->wds_type;
+ break;
+
+ case PRISM2_PARAM_HOSTSCAN:
+ ret = -EOPNOTSUPP;
+ break;
+
+ case PRISM2_PARAM_AP_SCAN:
+ *param = local->passive_scan_interval;
+ break;
+
+ case PRISM2_PARAM_ENH_SEC:
+ *param = local->enh_sec;
+ break;
+
+#ifdef PRISM2_IO_DEBUG
+ case PRISM2_PARAM_IO_DEBUG:
+ *param = local->io_debug_enabled;
+ break;
+#endif /* PRISM2_IO_DEBUG */
+
+ case PRISM2_PARAM_BASIC_RATES:
+ *param = local->basic_rates;
+ break;
+
+ case PRISM2_PARAM_OPER_RATES:
+ *param = local->tx_rate_control;
+ break;
+
+ case PRISM2_PARAM_HOSTAPD:
+ *param = local->hostapd;
+ break;
+
+ case PRISM2_PARAM_HOSTAPD_STA:
+ *param = local->hostapd_sta;
+ break;
+
+ case PRISM2_PARAM_WPA:
+ if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0))
+ ret = -EOPNOTSUPP;
+ *param = local->wpa;
+ break;
+
+ case PRISM2_PARAM_PRIVACY_INVOKED:
+ *param = local->privacy_invoked;
+ break;
+
+ case PRISM2_PARAM_TKIP_COUNTERMEASURES:
+ *param = local->tkip_countermeasures;
+ break;
+
+ case PRISM2_PARAM_DROP_UNENCRYPTED:
+ *param = local->drop_unencrypted;
+ break;
+
+ case PRISM2_PARAM_SCAN_CHANNEL_MASK:
+ *param = local->scan_channel_mask;
+ break;
+
+ default:
+ printk(KERN_DEBUG "%s: get_prism2_param: unknown param %d\n",
+ dev->name, *param);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+
+static int prism2_ioctl_priv_readmif(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 resp0;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, *extra, NULL,
+ &resp0))
+ return -EOPNOTSUPP;
+ else
+ *extra = resp0;
+
+ return 0;
+}
+
+
+static int prism2_ioctl_priv_writemif(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu, char *extra)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ u16 cr, val;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ cr = *extra;
+ val = *(extra + 1);
+ if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, cr, &val, NULL))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+
+static int prism2_ioctl_priv_monitor(struct net_device *dev, int *i)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int ret = 0;
+ u32 mode;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ printk(KERN_DEBUG "%s: process %d (%s) used deprecated iwpriv monitor "
+ "- update software to use iwconfig mode monitor\n",
+ dev->name, current->pid, current->comm);
+
+ /* Backward compatibility code - this can be removed at some point */
+
+ if (*i == 0) {
+ /* Disable monitor mode - old mode was not saved, so go to
+ * Master mode */
+ mode = IW_MODE_MASTER;
+ ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL);
+ } else if (*i == 1) {
+ /* netlink socket mode is not supported anymore since it did
+ * not separate different devices from each other and was not
+ * best method for delivering large amount of packets to
+ * user space */
+ ret = -EOPNOTSUPP;
+ } else if (*i == 2 || *i == 3) {
+ switch (*i) {
+ case 2:
+ local->monitor_type = PRISM2_MONITOR_80211;
+ break;
+ case 3:
+ local->monitor_type = PRISM2_MONITOR_PRISM;
+ break;
+ }
+ mode = IW_MODE_MONITOR;
+ ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL);
+ hostap_monitor_mode_enable(local);
+ } else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+
+static int prism2_ioctl_priv_reset(struct net_device *dev, int *i)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ printk(KERN_DEBUG "%s: manual reset request(%d)\n", dev->name, *i);
+ switch (*i) {
+ case 0:
+ /* Disable and enable card */
+ local->func->hw_shutdown(dev, 1);
+ local->func->hw_config(dev, 0);
+ break;
+
+ case 1:
+ /* COR sreset */
+ local->func->hw_reset(dev);
+ break;
+
+ case 2:
+ /* Disable and enable port 0 */
+ local->func->reset_port(dev);
+ break;
+
+ case 3:
+ prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING);
+ if (local->func->cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL,
+ NULL))
+ return -EINVAL;
+ break;
+
+ case 4:
+ if (local->func->cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL,
+ NULL))
+ return -EINVAL;
+ break;
+
+ default:
+ printk(KERN_DEBUG "Unknown reset request %d\n", *i);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+
+static int prism2_ioctl_priv_set_rid_word(struct net_device *dev, int *i)
+{
+ int rid = *i;
+ int value = *(i + 1);
+
+ printk(KERN_DEBUG "%s: Set RID[0x%X] = %d\n", dev->name, rid, value);
+
+ if (hostap_set_word(dev, rid, value))
+ return -EINVAL;
+
+ return 0;
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+static int ap_mac_cmd_ioctl(local_info_t *local, int *cmd)
+{
+ int ret = 0;
+
+ switch (*cmd) {
+ case AP_MAC_CMD_POLICY_OPEN:
+ local->ap->mac_restrictions.policy = MAC_POLICY_OPEN;
+ break;
+ case AP_MAC_CMD_POLICY_ALLOW:
+ local->ap->mac_restrictions.policy = MAC_POLICY_ALLOW;
+ break;
+ case AP_MAC_CMD_POLICY_DENY:
+ local->ap->mac_restrictions.policy = MAC_POLICY_DENY;
+ break;
+ case AP_MAC_CMD_FLUSH:
+ ap_control_flush_macs(&local->ap->mac_restrictions);
+ break;
+ case AP_MAC_CMD_KICKALL:
+ ap_control_kickall(local->ap);
+ hostap_deauth_all_stas(local->dev, local->ap, 0);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+static int prism2_ioctl_priv_download(local_info_t *local, struct iw_point *p)
+{
+ struct prism2_download_param *param;
+ int ret = 0;
+
+ if (p->length < sizeof(struct prism2_download_param) ||
+ p->length > 1024 || !p->pointer)
+ return -EINVAL;
+
+ param = (struct prism2_download_param *)
+ kmalloc(p->length, GFP_KERNEL);
+ if (param == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(param, p->pointer, p->length)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (p->length < sizeof(struct prism2_download_param) +
+ param->num_areas * sizeof(struct prism2_download_area)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = local->func->download(local, param);
+
+ out:
+ if (param != NULL)
+ kfree(param);
+
+ return ret;
+}
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+
+static int prism2_set_genericelement(struct net_device *dev, u8 *elem,
+ size_t len)
+{
+ struct hostap_interface *iface = dev->priv;
+ local_info_t *local = iface->local;
+ u8 *buf;
+
+ /*
+ * Add 16-bit length in the beginning of the buffer because Prism2 RID
+ * includes it.
+ */
+ buf = kmalloc(len + 2, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ *((u16 *) buf) = cpu_to_le16(len);
+ memcpy(buf + 2, elem, len);
+
+ kfree(local->generic_elem);
+ local->generic_elem = buf;
+ local->generic_elem_len = len + 2;
+
+ return local->func->set_rid(local->dev, HFA384X_RID_GENERICELEMENT,
+ buf, len + 2);
+}
+
+
+static int prism2_ioctl_siwauth(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *data, char *extra)
+{
+ struct hostap_interface *iface = dev->priv;
+ local_info_t *local = iface->local;
+
+ switch (data->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * Host AP driver does not use these parameters and allows
+ * wpa_supplicant to control them internally.
+ */
+ break;
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ local->tkip_countermeasures = data->value;
+ break;
+ case IW_AUTH_DROP_UNENCRYPTED:
+ local->drop_unencrypted = data->value;
+ break;
+ case IW_AUTH_80211_AUTH_ALG:
+ local->auth_algs = data->value;
+ break;
+ case IW_AUTH_WPA_ENABLED:
+ if (data->value == 0) {
+ local->wpa = 0;
+ if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0))
+ break;
+ prism2_set_genericelement(dev, "", 0);
+ local->host_roaming = 0;
+ local->privacy_invoked = 0;
+ if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE,
+ 0) ||
+ hostap_set_roaming(local) ||
+ hostap_set_encryption(local) ||
+ local->func->reset_port(dev))
+ return -EINVAL;
+ break;
+ }
+ if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0))
+ return -EOPNOTSUPP;
+ local->host_roaming = 2;
+ local->privacy_invoked = 1;
+ local->wpa = 1;
+ if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1) ||
+ hostap_set_roaming(local) ||
+ hostap_set_encryption(local) ||
+ local->func->reset_port(dev))
+ return -EINVAL;
+ break;
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ local->ieee_802_1x = data->value;
+ break;
+ case IW_AUTH_PRIVACY_INVOKED:
+ local->privacy_invoked = data->value;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+
+static int prism2_ioctl_giwauth(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *data, char *extra)
+{
+ struct hostap_interface *iface = dev->priv;
+ local_info_t *local = iface->local;
+
+ switch (data->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * Host AP driver does not use these parameters and allows
+ * wpa_supplicant to control them internally.
+ */
+ return -EOPNOTSUPP;
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ data->value = local->tkip_countermeasures;
+ break;
+ case IW_AUTH_DROP_UNENCRYPTED:
+ data->value = local->drop_unencrypted;
+ break;
+ case IW_AUTH_80211_AUTH_ALG:
+ data->value = local->auth_algs;
+ break;
+ case IW_AUTH_WPA_ENABLED:
+ data->value = local->wpa;
+ break;
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ data->value = local->ieee_802_1x;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+
+static int prism2_ioctl_siwencodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq, char *extra)
+{
+ struct hostap_interface *iface = dev->priv;
+ local_info_t *local = iface->local;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
+ int i, ret = 0;
+ struct ieee80211_crypto_ops *ops;
+ struct ieee80211_crypt_data **crypt;
+ void *sta_ptr;
+ u8 *addr;
+ const char *alg, *module;
+
+ i = erq->flags & IW_ENCODE_INDEX;
+ if (i > WEP_KEYS)
+ return -EINVAL;
+ if (i < 1 || i > WEP_KEYS)
+ i = local->tx_keyidx;
+ else
+ i--;
+ if (i < 0 || i >= WEP_KEYS)
+ return -EINVAL;
+
+ addr = ext->addr.sa_data;
+ if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff &&
+ addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) {
+ sta_ptr = NULL;
+ crypt = &local->crypt[i];
+ } else {
+ if (i != 0)
+ return -EINVAL;
+ sta_ptr = ap_crypt_get_ptrs(local->ap, addr, 0, &crypt);
+ if (sta_ptr == NULL) {
+ if (local->iw_mode == IW_MODE_INFRA) {
+ /*
+ * TODO: add STA entry for the current AP so
+ * that unicast key can be used. For now, this
+ * is emulated by using default key idx 0.
+ */
+ i = 0;
+ crypt = &local->crypt[i];
+ } else
+ return -EINVAL;
+ }
+ }
+
+ if ((erq->flags & IW_ENCODE_DISABLED) ||
+ ext->alg == IW_ENCODE_ALG_NONE) {
+ if (*crypt)
+ prism2_crypt_delayed_deinit(local, crypt);
+ goto done;
+ }
+
+ switch (ext->alg) {
+ case IW_ENCODE_ALG_WEP:
+ alg = "WEP";
+ module = "ieee80211_crypt_wep";
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ alg = "TKIP";
+ module = "ieee80211_crypt_tkip";
+ break;
+ case IW_ENCODE_ALG_CCMP:
+ alg = "CCMP";
+ module = "ieee80211_crypt_ccmp";
+ break;
+ default:
+ printk(KERN_DEBUG "%s: unsupported algorithm %d\n",
+ local->dev->name, ext->alg);
+ ret = -EOPNOTSUPP;
+ goto done;
+ }
+
+ ops = ieee80211_get_crypto_ops(alg);
+ if (ops == NULL) {
+ request_module(module);
+ ops = ieee80211_get_crypto_ops(alg);
+ }
+ if (ops == NULL) {
+ printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n",
+ local->dev->name, alg);
+ ret = -EOPNOTSUPP;
+ goto done;
+ }
+
+ if (sta_ptr || ext->alg != IW_ENCODE_ALG_WEP) {
+ /*
+ * Per station encryption and other than WEP algorithms
+ * require host-based encryption, so force them on
+ * automatically.
+ */
+ local->host_decrypt = local->host_encrypt = 1;
+ }
+
+ if (*crypt == NULL || (*crypt)->ops != ops) {
+ struct ieee80211_crypt_data *new_crypt;
+
+ prism2_crypt_delayed_deinit(local, crypt);
+
+ new_crypt = (struct ieee80211_crypt_data *)
+ kmalloc(sizeof(struct ieee80211_crypt_data),
+ GFP_KERNEL);
+ if (new_crypt == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
+ new_crypt->ops = ops;
+ new_crypt->priv = new_crypt->ops->init(i);
+ if (new_crypt->priv == NULL) {
+ kfree(new_crypt);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ *crypt = new_crypt;
+ }
+
+ /*
+ * TODO: if ext_flags does not have IW_ENCODE_EXT_RX_SEQ_VALID, the
+ * existing seq# should not be changed.
+ * TODO: if ext_flags has IW_ENCODE_EXT_TX_SEQ_VALID, next TX seq#
+ * should be changed to something else than zero.
+ */
+ if ((!(ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) || ext->key_len > 0)
+ && (*crypt)->ops->set_key &&
+ (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
+ (*crypt)->priv) < 0) {
+ printk(KERN_DEBUG "%s: key setting failed\n",
+ local->dev->name);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ if (!sta_ptr)
+ local->tx_keyidx = i;
+ else if (i) {
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+
+ if (sta_ptr == NULL && ext->key_len > 0) {
+ int first = 1, j;
+ for (j = 0; j < WEP_KEYS; j++) {
+ if (j != i && local->crypt[j]) {
+ first = 0;
+ break;
+ }
+ }
+ if (first)
+ local->tx_keyidx = i;
+ }
+
+ done:
+ if (sta_ptr)
+ hostap_handle_sta_release(sta_ptr);
+
+ local->open_wep = erq->flags & IW_ENCODE_OPEN;
+
+ /*
+ * Do not reset port0 if card is in Managed mode since resetting will
+ * generate new IEEE 802.11 authentication which may end up in looping
+ * with IEEE 802.1X. Prism2 documentation seem to require port reset
+ * after WEP configuration. However, keys are apparently changed at
+ * least in Managed mode.
+ */
+ if (ret == 0 &&
+ (hostap_set_encryption(local) ||
+ (local->iw_mode != IW_MODE_INFRA &&
+ local->func->reset_port(local->dev))))
+ ret = -EINVAL;
+
+ return ret;
+}
+
+
+static int prism2_ioctl_giwencodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq, char *extra)
+{
+ struct hostap_interface *iface = dev->priv;
+ local_info_t *local = iface->local;
+ struct ieee80211_crypt_data **crypt;
+ void *sta_ptr;
+ int max_key_len, i;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
+ u8 *addr;
+
+ max_key_len = erq->length - sizeof(*ext);
+ if (max_key_len < 0)
+ return -EINVAL;
+
+ i = erq->flags & IW_ENCODE_INDEX;
+ if (i < 1 || i > WEP_KEYS)
+ i = local->tx_keyidx;
+ else
+ i--;
+
+ addr = ext->addr.sa_data;
+ if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff &&
+ addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) {
+ sta_ptr = NULL;
+ crypt = &local->crypt[i];
+ } else {
+ i = 0;
+ sta_ptr = ap_crypt_get_ptrs(local->ap, addr, 0, &crypt);
+ if (sta_ptr == NULL)
+ return -EINVAL;
+ }
+ erq->flags = i + 1;
+ memset(ext, 0, sizeof(*ext));
+
+ if (*crypt == NULL || (*crypt)->ops == NULL) {
+ ext->alg = IW_ENCODE_ALG_NONE;
+ ext->key_len = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ } else {
+ if (strcmp((*crypt)->ops->name, "WEP") == 0)
+ ext->alg = IW_ENCODE_ALG_WEP;
+ else if (strcmp((*crypt)->ops->name, "TKIP") == 0)
+ ext->alg = IW_ENCODE_ALG_TKIP;
+ else if (strcmp((*crypt)->ops->name, "CCMP") == 0)
+ ext->alg = IW_ENCODE_ALG_CCMP;
+ else
+ return -EINVAL;
+
+ if ((*crypt)->ops->get_key) {
+ ext->key_len =
+ (*crypt)->ops->get_key(ext->key,
+ max_key_len,
+ ext->tx_seq,
+ (*crypt)->priv);
+ if (ext->key_len &&
+ (ext->alg == IW_ENCODE_ALG_TKIP ||
+ ext->alg == IW_ENCODE_ALG_CCMP))
+ ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
+ }
+ }
+
+ if (sta_ptr)
+ hostap_handle_sta_release(sta_ptr);
+
+ return 0;
+}
+
+
+static int prism2_ioctl_set_encryption(local_info_t *local,
+ struct prism2_hostapd_param *param,
+ int param_len)
+{
+ int ret = 0;
+ struct ieee80211_crypto_ops *ops;
+ struct ieee80211_crypt_data **crypt;
+ void *sta_ptr;
+
+ param->u.crypt.err = 0;
+ param->u.crypt.alg[HOSTAP_CRYPT_ALG_NAME_LEN - 1] = '\0';
+
+ if (param_len !=
+ (int) ((char *) param->u.crypt.key - (char *) param) +
+ param->u.crypt.key_len)
+ return -EINVAL;
+
+ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (param->u.crypt.idx >= WEP_KEYS)
+ return -EINVAL;
+ sta_ptr = NULL;
+ crypt = &local->crypt[param->u.crypt.idx];
+ } else {
+ if (param->u.crypt.idx)
+ return -EINVAL;
+ sta_ptr = ap_crypt_get_ptrs(
+ local->ap, param->sta_addr,
+ (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_PERMANENT),
+ &crypt);
+
+ if (sta_ptr == NULL) {
+ param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR;
+ return -EINVAL;
+ }
+ }
+
+ if (strcmp(param->u.crypt.alg, "none") == 0) {
+ if (crypt)
+ prism2_crypt_delayed_deinit(local, crypt);
+ goto done;
+ }
+
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) {
+ request_module("ieee80211_crypt_wep");
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) {
+ request_module("ieee80211_crypt_tkip");
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) {
+ request_module("ieee80211_crypt_ccmp");
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ }
+ if (ops == NULL) {
+ printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n",
+ local->dev->name, param->u.crypt.alg);
+ param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ALG;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* station based encryption and other than WEP algorithms require
+ * host-based encryption, so force them on automatically */
+ local->host_decrypt = local->host_encrypt = 1;
+
+ if (*crypt == NULL || (*crypt)->ops != ops) {
+ struct ieee80211_crypt_data *new_crypt;
+
+ prism2_crypt_delayed_deinit(local, crypt);
+
+ new_crypt = (struct ieee80211_crypt_data *)
+ kmalloc(sizeof(struct ieee80211_crypt_data),
+ GFP_KERNEL);
+ if (new_crypt == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
+ new_crypt->ops = ops;
+ new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx);
+ if (new_crypt->priv == NULL) {
+ kfree(new_crypt);
+ param->u.crypt.err =
+ HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ *crypt = new_crypt;
+ }
+
+ if ((!(param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) ||
+ param->u.crypt.key_len > 0) && (*crypt)->ops->set_key &&
+ (*crypt)->ops->set_key(param->u.crypt.key,
+ param->u.crypt.key_len, param->u.crypt.seq,
+ (*crypt)->priv) < 0) {
+ printk(KERN_DEBUG "%s: key setting failed\n",
+ local->dev->name);
+ param->u.crypt.err = HOSTAP_CRYPT_ERR_KEY_SET_FAILED;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) {
+ if (!sta_ptr)
+ local->tx_keyidx = param->u.crypt.idx;
+ else if (param->u.crypt.idx) {
+ printk(KERN_DEBUG "%s: TX key idx setting failed\n",
+ local->dev->name);
+ param->u.crypt.err =
+ HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED;
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ done:
+ if (sta_ptr)
+ hostap_handle_sta_release(sta_ptr);
+
+ /* Do not reset port0 if card is in Managed mode since resetting will
+ * generate new IEEE 802.11 authentication which may end up in looping
+ * with IEEE 802.1X. Prism2 documentation seem to require port reset
+ * after WEP configuration. However, keys are apparently changed at
+ * least in Managed mode. */
+ if (ret == 0 &&
+ (hostap_set_encryption(local) ||
+ (local->iw_mode != IW_MODE_INFRA &&
+ local->func->reset_port(local->dev)))) {
+ param->u.crypt.err = HOSTAP_CRYPT_ERR_CARD_CONF_FAILED;
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+
+static int prism2_ioctl_get_encryption(local_info_t *local,
+ struct prism2_hostapd_param *param,
+ int param_len)
+{
+ struct ieee80211_crypt_data **crypt;
+ void *sta_ptr;
+ int max_key_len;
+
+ param->u.crypt.err = 0;
+
+ max_key_len = param_len -
+ (int) ((char *) param->u.crypt.key - (char *) param);
+ if (max_key_len < 0)
+ return -EINVAL;
+
+ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ sta_ptr = NULL;
+ if (param->u.crypt.idx >= WEP_KEYS)
+ param->u.crypt.idx = local->tx_keyidx;
+ crypt = &local->crypt[param->u.crypt.idx];
+ } else {
+ param->u.crypt.idx = 0;
+ sta_ptr = ap_crypt_get_ptrs(local->ap, param->sta_addr, 0,
+ &crypt);
+
+ if (sta_ptr == NULL) {
+ param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR;
+ return -EINVAL;
+ }
+ }
+
+ if (*crypt == NULL || (*crypt)->ops == NULL) {
+ memcpy(param->u.crypt.alg, "none", 5);
+ param->u.crypt.key_len = 0;
+ param->u.crypt.idx = 0xff;
+ } else {
+ strncpy(param->u.crypt.alg, (*crypt)->ops->name,
+ HOSTAP_CRYPT_ALG_NAME_LEN);
+ param->u.crypt.key_len = 0;
+
+ memset(param->u.crypt.seq, 0, 8);
+ if ((*crypt)->ops->get_key) {
+ param->u.crypt.key_len =
+ (*crypt)->ops->get_key(param->u.crypt.key,
+ max_key_len,
+ param->u.crypt.seq,
+ (*crypt)->priv);
+ }
+ }
+
+ if (sta_ptr)
+ hostap_handle_sta_release(sta_ptr);
+
+ return 0;
+}
+
+
+static int prism2_ioctl_get_rid(local_info_t *local,
+ struct prism2_hostapd_param *param,
+ int param_len)
+{
+ int max_len, res;
+
+ max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN;
+ if (max_len < 0)
+ return -EINVAL;
+
+ res = local->func->get_rid(local->dev, param->u.rid.rid,
+ param->u.rid.data, param->u.rid.len, 0);
+ if (res >= 0) {
+ param->u.rid.len = res;
+ return 0;
+ }
+
+ return res;
+}
+
+
+static int prism2_ioctl_set_rid(local_info_t *local,
+ struct prism2_hostapd_param *param,
+ int param_len)
+{
+ int max_len;
+
+ max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN;
+ if (max_len < 0 || max_len < param->u.rid.len)
+ return -EINVAL;
+
+ return local->func->set_rid(local->dev, param->u.rid.rid,
+ param->u.rid.data, param->u.rid.len);
+}
+
+
+static int prism2_ioctl_set_assoc_ap_addr(local_info_t *local,
+ struct prism2_hostapd_param *param,
+ int param_len)
+{
+ printk(KERN_DEBUG "%ssta: associated as client with AP " MACSTR "\n",
+ local->dev->name, MAC2STR(param->sta_addr));
+ memcpy(local->assoc_ap_addr, param->sta_addr, ETH_ALEN);
+ return 0;
+}
+
+
+static int prism2_ioctl_siwgenie(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ return prism2_set_genericelement(dev, extra, data->length);
+}
+
+
+static int prism2_ioctl_giwgenie(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct hostap_interface *iface = dev->priv;
+ local_info_t *local = iface->local;
+ int len = local->generic_elem_len - 2;
+
+ if (len <= 0 || local->generic_elem == NULL) {
+ data->length = 0;
+ return 0;
+ }
+
+ if (data->length < len)
+ return -E2BIG;
+
+ data->length = len;
+ memcpy(extra, local->generic_elem + 2, len);
+
+ return 0;
+}
+
+
+static int prism2_ioctl_set_generic_element(local_info_t *local,
+ struct prism2_hostapd_param *param,
+ int param_len)
+{
+ int max_len, len;
+
+ len = param->u.generic_elem.len;
+ max_len = param_len - PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN;
+ if (max_len < 0 || max_len < len)
+ return -EINVAL;
+
+ return prism2_set_genericelement(local->dev,
+ param->u.generic_elem.data, len);
+}
+
+
+static int prism2_ioctl_siwmlme(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct hostap_interface *iface = dev->priv;
+ local_info_t *local = iface->local;
+ struct iw_mlme *mlme = (struct iw_mlme *) extra;
+ u16 reason;
+
+ reason = cpu_to_le16(mlme->reason_code);
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ return prism2_sta_send_mgmt(local, mlme->addr.sa_data,
+ IEEE80211_STYPE_DEAUTH,
+ (u8 *) &reason, 2);
+ case IW_MLME_DISASSOC:
+ return prism2_sta_send_mgmt(local, mlme->addr.sa_data,
+ IEEE80211_STYPE_DISASSOC,
+ (u8 *) &reason, 2);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+
+static int prism2_ioctl_mlme(local_info_t *local,
+ struct prism2_hostapd_param *param)
+{
+ u16 reason;
+
+ reason = cpu_to_le16(param->u.mlme.reason_code);
+ switch (param->u.mlme.cmd) {
+ case MLME_STA_DEAUTH:
+ return prism2_sta_send_mgmt(local, param->sta_addr,
+ IEEE80211_STYPE_DEAUTH,
+ (u8 *) &reason, 2);
+ case MLME_STA_DISASSOC:
+ return prism2_sta_send_mgmt(local, param->sta_addr,
+ IEEE80211_STYPE_DISASSOC,
+ (u8 *) &reason, 2);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+
+static int prism2_ioctl_scan_req(local_info_t *local,
+ struct prism2_hostapd_param *param)
+{
+#ifndef PRISM2_NO_STATION_MODES
+ if ((local->iw_mode != IW_MODE_INFRA &&
+ local->iw_mode != IW_MODE_ADHOC) ||
+ (local->sta_fw_ver < PRISM2_FW_VER(1,3,1)))
+ return -EOPNOTSUPP;
+
+ if (!local->dev_enabled)
+ return -ENETDOWN;
+
+ return prism2_request_hostscan(local->dev, param->u.scan_req.ssid,
+ param->u.scan_req.ssid_len);
+#else /* PRISM2_NO_STATION_MODES */
+ return -EOPNOTSUPP;
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+
+static int prism2_ioctl_priv_hostapd(local_info_t *local, struct iw_point *p)
+{
+ struct prism2_hostapd_param *param;
+ int ret = 0;
+ int ap_ioctl = 0;
+
+ if (p->length < sizeof(struct prism2_hostapd_param) ||
+ p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer)
+ return -EINVAL;
+
+ param = (struct prism2_hostapd_param *) kmalloc(p->length, GFP_KERNEL);
+ if (param == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(param, p->pointer, p->length)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ switch (param->cmd) {
+ case PRISM2_SET_ENCRYPTION:
+ ret = prism2_ioctl_set_encryption(local, param, p->length);
+ break;
+ case PRISM2_GET_ENCRYPTION:
+ ret = prism2_ioctl_get_encryption(local, param, p->length);
+ break;
+ case PRISM2_HOSTAPD_GET_RID:
+ ret = prism2_ioctl_get_rid(local, param, p->length);
+ break;
+ case PRISM2_HOSTAPD_SET_RID:
+ ret = prism2_ioctl_set_rid(local, param, p->length);
+ break;
+ case PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR:
+ ret = prism2_ioctl_set_assoc_ap_addr(local, param, p->length);
+ break;
+ case PRISM2_HOSTAPD_SET_GENERIC_ELEMENT:
+ ret = prism2_ioctl_set_generic_element(local, param,
+ p->length);
+ break;
+ case PRISM2_HOSTAPD_MLME:
+ ret = prism2_ioctl_mlme(local, param);
+ break;
+ case PRISM2_HOSTAPD_SCAN_REQ:
+ ret = prism2_ioctl_scan_req(local, param);
+ break;
+ default:
+ ret = prism2_hostapd(local->ap, param);
+ ap_ioctl = 1;
+ break;
+ }
+
+ if (ret == 1 || !ap_ioctl) {
+ if (copy_to_user(p->pointer, param, p->length)) {
+ ret = -EFAULT;
+ goto out;
+ } else if (ap_ioctl)
+ ret = 0;
+ }
+
+ out:
+ if (param != NULL)
+ kfree(param);
+
+ return ret;
+}
+
+
+static void prism2_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ strncpy(info->driver, "hostap", sizeof(info->driver) - 1);
+ strncpy(info->version, PRISM2_VERSION,
+ sizeof(info->version) - 1);
+ snprintf(info->fw_version, sizeof(info->fw_version) - 1,
+ "%d.%d.%d", (local->sta_fw_ver >> 16) & 0xff,
+ (local->sta_fw_ver >> 8) & 0xff,
+ local->sta_fw_ver & 0xff);
+}
+
+static struct ethtool_ops prism2_ethtool_ops = {
+ .get_drvinfo = prism2_get_drvinfo
+};
+
+
+/* Structures to export the Wireless Handlers */
+
+static const iw_handler prism2_handler[] =
+{
+ (iw_handler) NULL, /* SIOCSIWCOMMIT */
+ (iw_handler) prism2_get_name, /* SIOCGIWNAME */
+ (iw_handler) NULL, /* SIOCSIWNWID */
+ (iw_handler) NULL, /* SIOCGIWNWID */
+ (iw_handler) prism2_ioctl_siwfreq, /* SIOCSIWFREQ */
+ (iw_handler) prism2_ioctl_giwfreq, /* SIOCGIWFREQ */
+ (iw_handler) prism2_ioctl_siwmode, /* SIOCSIWMODE */
+ (iw_handler) prism2_ioctl_giwmode, /* SIOCGIWMODE */
+ (iw_handler) prism2_ioctl_siwsens, /* SIOCSIWSENS */
+ (iw_handler) prism2_ioctl_giwsens, /* SIOCGIWSENS */
+ (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */
+ (iw_handler) prism2_ioctl_giwrange, /* SIOCGIWRANGE */
+ (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */
+ (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */
+ (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */
+ (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */
+ iw_handler_set_spy, /* SIOCSIWSPY */
+ iw_handler_get_spy, /* SIOCGIWSPY */
+ iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
+ iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
+ (iw_handler) prism2_ioctl_siwap, /* SIOCSIWAP */
+ (iw_handler) prism2_ioctl_giwap, /* SIOCGIWAP */
+ (iw_handler) prism2_ioctl_siwmlme, /* SIOCSIWMLME */
+ (iw_handler) prism2_ioctl_giwaplist, /* SIOCGIWAPLIST */
+ (iw_handler) prism2_ioctl_siwscan, /* SIOCSIWSCAN */
+ (iw_handler) prism2_ioctl_giwscan, /* SIOCGIWSCAN */
+ (iw_handler) prism2_ioctl_siwessid, /* SIOCSIWESSID */
+ (iw_handler) prism2_ioctl_giwessid, /* SIOCGIWESSID */
+ (iw_handler) prism2_ioctl_siwnickn, /* SIOCSIWNICKN */
+ (iw_handler) prism2_ioctl_giwnickn, /* SIOCGIWNICKN */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) prism2_ioctl_siwrate, /* SIOCSIWRATE */
+ (iw_handler) prism2_ioctl_giwrate, /* SIOCGIWRATE */
+ (iw_handler) prism2_ioctl_siwrts, /* SIOCSIWRTS */
+ (iw_handler) prism2_ioctl_giwrts, /* SIOCGIWRTS */
+ (iw_handler) prism2_ioctl_siwfrag, /* SIOCSIWFRAG */
+ (iw_handler) prism2_ioctl_giwfrag, /* SIOCGIWFRAG */
+ (iw_handler) prism2_ioctl_siwtxpow, /* SIOCSIWTXPOW */
+ (iw_handler) prism2_ioctl_giwtxpow, /* SIOCGIWTXPOW */
+ (iw_handler) prism2_ioctl_siwretry, /* SIOCSIWRETRY */
+ (iw_handler) prism2_ioctl_giwretry, /* SIOCGIWRETRY */
+ (iw_handler) prism2_ioctl_siwencode, /* SIOCSIWENCODE */
+ (iw_handler) prism2_ioctl_giwencode, /* SIOCGIWENCODE */
+ (iw_handler) prism2_ioctl_siwpower, /* SIOCSIWPOWER */
+ (iw_handler) prism2_ioctl_giwpower, /* SIOCGIWPOWER */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) prism2_ioctl_siwgenie, /* SIOCSIWGENIE */
+ (iw_handler) prism2_ioctl_giwgenie, /* SIOCGIWGENIE */
+ (iw_handler) prism2_ioctl_siwauth, /* SIOCSIWAUTH */
+ (iw_handler) prism2_ioctl_giwauth, /* SIOCGIWAUTH */
+ (iw_handler) prism2_ioctl_siwencodeext, /* SIOCSIWENCODEEXT */
+ (iw_handler) prism2_ioctl_giwencodeext, /* SIOCGIWENCODEEXT */
+ (iw_handler) NULL, /* SIOCSIWPMKSA */
+ (iw_handler) NULL, /* -- hole -- */
+};
+
+static const iw_handler prism2_private_handler[] =
+{ /* SIOCIWFIRSTPRIV + */
+ (iw_handler) prism2_ioctl_priv_prism2_param, /* 0 */
+ (iw_handler) prism2_ioctl_priv_get_prism2_param, /* 1 */
+ (iw_handler) prism2_ioctl_priv_writemif, /* 2 */
+ (iw_handler) prism2_ioctl_priv_readmif, /* 3 */
+};
+
+static const struct iw_handler_def hostap_iw_handler_def =
+{
+ .num_standard = sizeof(prism2_handler) / sizeof(iw_handler),
+ .num_private = sizeof(prism2_private_handler) / sizeof(iw_handler),
+ .num_private_args = sizeof(prism2_priv) / sizeof(struct iw_priv_args),
+ .standard = (iw_handler *) prism2_handler,
+ .private = (iw_handler *) prism2_private_handler,
+ .private_args = (struct iw_priv_args *) prism2_priv,
+ .get_wireless_stats = hostap_get_wireless_stats,
+};
+
+
+int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct iwreq *wrq = (struct iwreq *) ifr;
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int ret = 0;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ switch (cmd) {
+ /* Private ioctls (iwpriv) that have not yet been converted
+ * into new wireless extensions API */
+
+ case PRISM2_IOCTL_INQUIRE:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = prism2_ioctl_priv_inquire(dev, (int *) wrq->u.name);
+ break;
+
+ case PRISM2_IOCTL_MONITOR:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = prism2_ioctl_priv_monitor(dev, (int *) wrq->u.name);
+ break;
+
+ case PRISM2_IOCTL_RESET:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = prism2_ioctl_priv_reset(dev, (int *) wrq->u.name);
+ break;
+
+ case PRISM2_IOCTL_WDS_ADD:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = prism2_wds_add(local, wrq->u.ap_addr.sa_data, 1);
+ break;
+
+ case PRISM2_IOCTL_WDS_DEL:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = prism2_wds_del(local, wrq->u.ap_addr.sa_data, 1, 0);
+ break;
+
+ case PRISM2_IOCTL_SET_RID_WORD:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = prism2_ioctl_priv_set_rid_word(dev,
+ (int *) wrq->u.name);
+ break;
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+ case PRISM2_IOCTL_MACCMD:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = ap_mac_cmd_ioctl(local, (int *) wrq->u.name);
+ break;
+
+ case PRISM2_IOCTL_ADDMAC:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = ap_control_add_mac(&local->ap->mac_restrictions,
+ wrq->u.ap_addr.sa_data);
+ break;
+ case PRISM2_IOCTL_DELMAC:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = ap_control_del_mac(&local->ap->mac_restrictions,
+ wrq->u.ap_addr.sa_data);
+ break;
+ case PRISM2_IOCTL_KICKMAC:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = ap_control_kick_mac(local->ap, local->dev,
+ wrq->u.ap_addr.sa_data);
+ break;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+ /* Private ioctls that are not used with iwpriv;
+ * in SIOCDEVPRIVATE range */
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+ case PRISM2_IOCTL_DOWNLOAD:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = prism2_ioctl_priv_download(local, &wrq->u.data);
+ break;
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+ case PRISM2_IOCTL_HOSTAPD:
+ if (!capable(CAP_NET_ADMIN)) ret = -EPERM;
+ else ret = prism2_ioctl_priv_hostapd(local, &wrq->u.data);
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c
new file mode 100644
index 000000000000..4f567ef6178d
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_pci.c
@@ -0,0 +1,473 @@
+#define PRISM2_PCI
+
+/* Host AP driver's support for Intersil Prism2.5 PCI cards is based on
+ * driver patches from Reyk Floeter <reyk@vantronix.net> and
+ * Andy Warner <andyw@pobox.com> */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#include "hostap_wlan.h"
+
+
+static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
+static char *dev_info = "hostap_pci";
+
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Support for Intersil Prism2.5-based 802.11 wireless LAN "
+ "PCI cards.");
+MODULE_SUPPORTED_DEVICE("Intersil Prism2.5-based WLAN PCI cards");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PRISM2_VERSION);
+
+
+/* struct local_info::hw_priv */
+struct hostap_pci_priv {
+ void __iomem *mem_start;
+};
+
+
+/* FIX: do we need mb/wmb/rmb with memory operations? */
+
+
+static struct pci_device_id prism2_pci_id_table[] __devinitdata = {
+ /* Intersil Prism3 ISL3872 11Mb/s WLAN Controller */
+ { 0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID },
+ /* Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller */
+ { 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID },
+ /* Samsung MagicLAN SWL-2210P */
+ { 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID },
+ { 0 }
+};
+
+
+#ifdef PRISM2_IO_DEBUG
+
+static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
+ writeb(v, hw_priv->mem_start + a);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+ u8 v;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ spin_lock_irqsave(&local->lock, flags);
+ v = readb(hw_priv->mem_start + a);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
+ spin_unlock_irqrestore(&local->lock, flags);
+ return v;
+}
+
+static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
+ writew(v, hw_priv->mem_start + a);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+ u16 v;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ spin_lock_irqsave(&local->lock, flags);
+ v = readw(hw_priv->mem_start + a);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
+ spin_unlock_irqrestore(&local->lock, flags);
+ return v;
+}
+
+#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
+#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
+#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
+#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
+#define HFA384X_OUTW_DATA(v,a) hfa384x_outw_debug(dev, (a), cpu_to_le16((v)))
+#define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw_debug(dev, (a)))
+
+#else /* PRISM2_IO_DEBUG */
+
+static inline void hfa384x_outb(struct net_device *dev, int a, u8 v)
+{
+ struct hostap_interface *iface;
+ struct hostap_pci_priv *hw_priv;
+ iface = netdev_priv(dev);
+ hw_priv = iface->local->hw_priv;
+ writeb(v, hw_priv->mem_start + a);
+}
+
+static inline u8 hfa384x_inb(struct net_device *dev, int a)
+{
+ struct hostap_interface *iface;
+ struct hostap_pci_priv *hw_priv;
+ iface = netdev_priv(dev);
+ hw_priv = iface->local->hw_priv;
+ return readb(hw_priv->mem_start + a);
+}
+
+static inline void hfa384x_outw(struct net_device *dev, int a, u16 v)
+{
+ struct hostap_interface *iface;
+ struct hostap_pci_priv *hw_priv;
+ iface = netdev_priv(dev);
+ hw_priv = iface->local->hw_priv;
+ writew(v, hw_priv->mem_start + a);
+}
+
+static inline u16 hfa384x_inw(struct net_device *dev, int a)
+{
+ struct hostap_interface *iface;
+ struct hostap_pci_priv *hw_priv;
+ iface = netdev_priv(dev);
+ hw_priv = iface->local->hw_priv;
+ return readw(hw_priv->mem_start + a);
+}
+
+#define HFA384X_OUTB(v,a) hfa384x_outb(dev, (a), (v))
+#define HFA384X_INB(a) hfa384x_inb(dev, (a))
+#define HFA384X_OUTW(v,a) hfa384x_outw(dev, (a), (v))
+#define HFA384X_INW(a) hfa384x_inw(dev, (a))
+#define HFA384X_OUTW_DATA(v,a) hfa384x_outw(dev, (a), cpu_to_le16((v)))
+#define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw(dev, (a)))
+
+#endif /* PRISM2_IO_DEBUG */
+
+
+static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
+ int len)
+{
+ u16 d_off;
+ u16 *pos;
+
+ d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
+ pos = (u16 *) buf;
+
+ for ( ; len > 1; len -= 2)
+ *pos++ = HFA384X_INW_DATA(d_off);
+
+ if (len & 1)
+ *((char *) pos) = HFA384X_INB(d_off);
+
+ return 0;
+}
+
+
+static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
+{
+ u16 d_off;
+ u16 *pos;
+
+ d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
+ pos = (u16 *) buf;
+
+ for ( ; len > 1; len -= 2)
+ HFA384X_OUTW_DATA(*pos++, d_off);
+
+ if (len & 1)
+ HFA384X_OUTB(*((char *) pos), d_off);
+
+ return 0;
+}
+
+
+/* FIX: This might change at some point.. */
+#include "hostap_hw.c"
+
+static void prism2_pci_cor_sreset(local_info_t *local)
+{
+ struct net_device *dev = local->dev;
+ u16 reg;
+
+ reg = HFA384X_INB(HFA384X_PCICOR_OFF);
+ printk(KERN_DEBUG "%s: Original COR value: 0x%0x\n", dev->name, reg);
+
+ /* linux-wlan-ng uses extremely long hold and settle times for
+ * COR sreset. A comment in the driver code mentions that the long
+ * delays appear to be necessary. However, at least IBM 22P6901 seems
+ * to work fine with shorter delays.
+ *
+ * Longer delays can be configured by uncommenting following line: */
+/* #define PRISM2_PCI_USE_LONG_DELAYS */
+
+#ifdef PRISM2_PCI_USE_LONG_DELAYS
+ int i;
+
+ HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF);
+ mdelay(250);
+
+ HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF);
+ mdelay(500);
+
+ /* Wait for f/w to complete initialization (CMD:BUSY == 0) */
+ i = 2000000 / 10;
+ while ((HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) && --i)
+ udelay(10);
+
+#else /* PRISM2_PCI_USE_LONG_DELAYS */
+
+ HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF);
+ mdelay(2);
+ HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF);
+ mdelay(2);
+
+#endif /* PRISM2_PCI_USE_LONG_DELAYS */
+
+ if (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) {
+ printk(KERN_DEBUG "%s: COR sreset timeout\n", dev->name);
+ }
+}
+
+
+static void prism2_pci_genesis_reset(local_info_t *local, int hcr)
+{
+ struct net_device *dev = local->dev;
+
+ HFA384X_OUTW(0x00C5, HFA384X_PCICOR_OFF);
+ mdelay(10);
+ HFA384X_OUTW(hcr, HFA384X_PCIHCR_OFF);
+ mdelay(10);
+ HFA384X_OUTW(0x0045, HFA384X_PCICOR_OFF);
+ mdelay(10);
+}
+
+
+static struct prism2_helper_functions prism2_pci_funcs =
+{
+ .card_present = NULL,
+ .cor_sreset = prism2_pci_cor_sreset,
+ .dev_open = NULL,
+ .dev_close = NULL,
+ .genesis_reset = prism2_pci_genesis_reset,
+ .hw_type = HOSTAP_HW_PCI,
+};
+
+
+static int prism2_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ unsigned long phymem;
+ void __iomem *mem = NULL;
+ local_info_t *local = NULL;
+ struct net_device *dev = NULL;
+ static int cards_found /* = 0 */;
+ int irq_registered = 0;
+ struct hostap_interface *iface;
+ struct hostap_pci_priv *hw_priv;
+
+ hw_priv = kmalloc(sizeof(*hw_priv), GFP_KERNEL);
+ if (hw_priv == NULL)
+ return -ENOMEM;
+ memset(hw_priv, 0, sizeof(*hw_priv));
+
+ if (pci_enable_device(pdev))
+ return -EIO;
+
+ phymem = pci_resource_start(pdev, 0);
+
+ if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) {
+ printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n");
+ goto err_out_disable;
+ }
+
+ mem = ioremap(phymem, pci_resource_len(pdev, 0));
+ if (mem == NULL) {
+ printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ;
+ goto fail;
+ }
+
+ dev = prism2_init_local_data(&prism2_pci_funcs, cards_found,
+ &pdev->dev);
+ if (dev == NULL)
+ goto fail;
+ iface = netdev_priv(dev);
+ local = iface->local;
+ local->hw_priv = hw_priv;
+ cards_found++;
+
+ dev->irq = pdev->irq;
+ hw_priv->mem_start = mem;
+
+ prism2_pci_cor_sreset(local);
+
+ pci_set_drvdata(pdev, dev);
+
+ if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name,
+ dev)) {
+ printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
+ goto fail;
+ } else
+ irq_registered = 1;
+
+ if (!local->pri_only && prism2_hw_config(dev, 1)) {
+ printk(KERN_DEBUG "%s: hardware initialization failed\n",
+ dev_info);
+ goto fail;
+ }
+
+ printk(KERN_INFO "%s: Intersil Prism2.5 PCI: "
+ "mem=0x%lx, irq=%d\n", dev->name, phymem, dev->irq);
+
+ return hostap_hw_ready(dev);
+
+ fail:
+ kfree(hw_priv);
+
+ if (irq_registered && dev)
+ free_irq(dev->irq, dev);
+
+ if (mem)
+ iounmap(mem);
+
+ release_mem_region(phymem, pci_resource_len(pdev, 0));
+
+ err_out_disable:
+ pci_disable_device(pdev);
+ kfree(hw_priv);
+ if (local)
+ local->hw_priv = NULL;
+ prism2_free_local_data(dev);
+
+ return -ENODEV;
+}
+
+
+static void prism2_pci_remove(struct pci_dev *pdev)
+{
+ struct net_device *dev;
+ struct hostap_interface *iface;
+ void __iomem *mem_start;
+ struct hostap_pci_priv *hw_priv;
+
+ dev = pci_get_drvdata(pdev);
+ iface = netdev_priv(dev);
+ hw_priv = iface->local->hw_priv;
+
+ /* Reset the hardware, and ensure interrupts are disabled. */
+ prism2_pci_cor_sreset(iface->local);
+ hfa384x_disable_interrupts(dev);
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+
+ mem_start = hw_priv->mem_start;
+ kfree(hw_priv);
+ iface->local->hw_priv = NULL;
+ prism2_free_local_data(dev);
+
+ iounmap(mem_start);
+
+ release_mem_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ pci_disable_device(pdev);
+}
+
+
+#ifdef CONFIG_PM
+static int prism2_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ if (netif_running(dev)) {
+ netif_stop_queue(dev);
+ netif_device_detach(dev);
+ }
+ prism2_suspend(dev);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, 3);
+
+ return 0;
+}
+
+static int prism2_pci_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ pci_enable_device(pdev);
+ pci_restore_state(pdev);
+ prism2_hw_config(dev, 0);
+ if (netif_running(dev)) {
+ netif_device_attach(dev);
+ netif_start_queue(dev);
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+
+MODULE_DEVICE_TABLE(pci, prism2_pci_id_table);
+
+static struct pci_driver prism2_pci_drv_id = {
+ .name = "prism2_pci",
+ .id_table = prism2_pci_id_table,
+ .probe = prism2_pci_probe,
+ .remove = prism2_pci_remove,
+#ifdef CONFIG_PM
+ .suspend = prism2_pci_suspend,
+ .resume = prism2_pci_resume,
+#endif /* CONFIG_PM */
+ /* Linux 2.4.6 added save_state and enable_wake that are not used here
+ */
+};
+
+
+static int __init init_prism2_pci(void)
+{
+ printk(KERN_INFO "%s: %s\n", dev_info, version);
+
+ return pci_register_driver(&prism2_pci_drv_id);
+}
+
+
+static void __exit exit_prism2_pci(void)
+{
+ pci_unregister_driver(&prism2_pci_drv_id);
+ printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
+}
+
+
+module_init(init_prism2_pci);
+module_exit(exit_prism2_pci);
diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c
new file mode 100644
index 000000000000..474ef83d813e
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_plx.c
@@ -0,0 +1,645 @@
+#define PRISM2_PLX
+
+/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is
+ * based on:
+ * - Host AP driver patch from james@madingley.org
+ * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc.
+ */
+
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#include "hostap_wlan.h"
+
+
+static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
+static char *dev_info = "hostap_plx";
+
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
+ "cards (PLX).");
+MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PRISM2_VERSION);
+
+
+static int ignore_cis;
+module_param(ignore_cis, int, 0444);
+MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
+
+
+/* struct local_info::hw_priv */
+struct hostap_plx_priv {
+ void __iomem *attr_mem;
+ unsigned int cor_offset;
+};
+
+
+#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */
+#define COR_SRESET 0x80
+#define COR_LEVLREQ 0x40
+#define COR_ENABLE_FUNC 0x01
+/* PCI Configuration Registers */
+#define PLX_PCIIPR 0x3d /* PCI Interrupt Pin */
+/* Local Configuration Registers */
+#define PLX_INTCSR 0x4c /* Interrupt Control/Status Register */
+#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */
+#define PLX_CNTRL 0x50
+#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28)
+
+
+#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
+
+static struct pci_device_id prism2_plx_id_table[] __devinitdata = {
+ PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
+ PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
+ PLXDEV(0x126c, 0x8030, "Nortel emobility"),
+ PLXDEV(0x1385, 0x4100, "Netgear MA301"),
+ PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"),
+ PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"),
+ PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"),
+ PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"),
+ PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"),
+ PLXDEV(0x16ab, 0x1103, "Longshine 8031"),
+ PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"),
+ PLXDEV(0xec80, 0xec00, "Belkin F5D6000"),
+ { 0 }
+};
+
+
+/* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid
+ * is not listed here, you will need to add it here to get the driver
+ * initialized. */
+static struct prism2_plx_manfid {
+ u16 manfid1, manfid2;
+} prism2_plx_known_manfids[] = {
+ { 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */,
+ { 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */,
+ { 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */,
+ { 0x0126, 0x8000 } /* Proxim RangeLAN */,
+ { 0x0138, 0x0002 } /* Compaq WL100 */,
+ { 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */,
+ { 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */,
+ { 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */,
+ { 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */,
+ { 0x028a, 0x0002 } /* D-Link DRC-650 */,
+ { 0x0250, 0x0002 } /* Samsung SWL2000-N */,
+ { 0xc250, 0x0002 } /* EMTAC A2424i */,
+ { 0xd601, 0x0002 } /* Z-Com XI300 */,
+ { 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */,
+ { 0, 0}
+};
+
+
+#ifdef PRISM2_IO_DEBUG
+
+static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
+ outb(v, dev->base_addr + a);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+ u8 v;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ spin_lock_irqsave(&local->lock, flags);
+ v = inb(dev->base_addr + a);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
+ spin_unlock_irqrestore(&local->lock, flags);
+ return v;
+}
+
+static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
+ outw(v, dev->base_addr + a);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+ u16 v;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ spin_lock_irqsave(&local->lock, flags);
+ v = inw(dev->base_addr + a);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
+ spin_unlock_irqrestore(&local->lock, flags);
+ return v;
+}
+
+static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
+ u8 *buf, int wc)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
+ outsw(dev->base_addr + a, buf, wc);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline void hfa384x_insw_debug(struct net_device *dev, int a,
+ u8 *buf, int wc)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ unsigned long flags;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
+ insw(dev->base_addr + a, buf, wc);
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
+#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
+#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
+#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
+#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
+#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
+
+#else /* PRISM2_IO_DEBUG */
+
+#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
+#define HFA384X_INB(a) inb(dev->base_addr + (a))
+#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
+#define HFA384X_INW(a) inw(dev->base_addr + (a))
+#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
+#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
+
+#endif /* PRISM2_IO_DEBUG */
+
+
+static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
+ int len)
+{
+ u16 d_off;
+ u16 *pos;
+
+ d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
+ pos = (u16 *) buf;
+
+ if (len / 2)
+ HFA384X_INSW(d_off, buf, len / 2);
+ pos += len / 2;
+
+ if (len & 1)
+ *((char *) pos) = HFA384X_INB(d_off);
+
+ return 0;
+}
+
+
+static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
+{
+ u16 d_off;
+ u16 *pos;
+
+ d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
+ pos = (u16 *) buf;
+
+ if (len / 2)
+ HFA384X_OUTSW(d_off, buf, len / 2);
+ pos += len / 2;
+
+ if (len & 1)
+ HFA384X_OUTB(*((char *) pos), d_off);
+
+ return 0;
+}
+
+
+/* FIX: This might change at some point.. */
+#include "hostap_hw.c"
+
+
+static void prism2_plx_cor_sreset(local_info_t *local)
+{
+ unsigned char corsave;
+ struct hostap_plx_priv *hw_priv = local->hw_priv;
+
+ printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n",
+ dev_info);
+
+ /* Set sreset bit of COR and clear it after hold time */
+
+ if (hw_priv->attr_mem == NULL) {
+ /* TMD7160 - COR at card's first I/O addr */
+ corsave = inb(hw_priv->cor_offset);
+ outb(corsave | COR_SRESET, hw_priv->cor_offset);
+ mdelay(2);
+ outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
+ mdelay(2);
+ } else {
+ /* PLX9052 */
+ corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
+ writeb(corsave | COR_SRESET,
+ hw_priv->attr_mem + hw_priv->cor_offset);
+ mdelay(2);
+ writeb(corsave & ~COR_SRESET,
+ hw_priv->attr_mem + hw_priv->cor_offset);
+ mdelay(2);
+ }
+}
+
+
+static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
+{
+ unsigned char corsave;
+ struct hostap_plx_priv *hw_priv = local->hw_priv;
+
+ if (hw_priv->attr_mem == NULL) {
+ /* TMD7160 - COR at card's first I/O addr */
+ corsave = inb(hw_priv->cor_offset);
+ outb(corsave | COR_SRESET, hw_priv->cor_offset);
+ mdelay(10);
+ outb(hcr, hw_priv->cor_offset + 2);
+ mdelay(10);
+ outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
+ mdelay(10);
+ } else {
+ /* PLX9052 */
+ corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
+ writeb(corsave | COR_SRESET,
+ hw_priv->attr_mem + hw_priv->cor_offset);
+ mdelay(10);
+ writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2);
+ mdelay(10);
+ writeb(corsave & ~COR_SRESET,
+ hw_priv->attr_mem + hw_priv->cor_offset);
+ mdelay(10);
+ }
+}
+
+
+static struct prism2_helper_functions prism2_plx_funcs =
+{
+ .card_present = NULL,
+ .cor_sreset = prism2_plx_cor_sreset,
+ .dev_open = NULL,
+ .dev_close = NULL,
+ .genesis_reset = prism2_plx_genesis_reset,
+ .hw_type = HOSTAP_HW_PLX,
+};
+
+
+static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len,
+ unsigned int *cor_offset,
+ unsigned int *cor_index)
+{
+#define CISTPL_CONFIG 0x1A
+#define CISTPL_MANFID 0x20
+#define CISTPL_END 0xFF
+#define CIS_MAX_LEN 256
+ u8 *cis;
+ int i, pos;
+ unsigned int rmsz, rasz, manfid1, manfid2;
+ struct prism2_plx_manfid *manfid;
+
+ cis = kmalloc(CIS_MAX_LEN, GFP_KERNEL);
+ if (cis == NULL)
+ return -ENOMEM;
+
+ /* read CIS; it is in even offsets in the beginning of attr_mem */
+ for (i = 0; i < CIS_MAX_LEN; i++)
+ cis[i] = readb(attr_mem + 2 * i);
+ printk(KERN_DEBUG "%s: CIS: %02x %02x %02x %02x %02x %02x ...\n",
+ dev_info, cis[0], cis[1], cis[2], cis[3], cis[4], cis[5]);
+
+ /* set reasonable defaults for Prism2 cards just in case CIS parsing
+ * fails */
+ *cor_offset = 0x3e0;
+ *cor_index = 0x01;
+ manfid1 = manfid2 = 0;
+
+ pos = 0;
+ while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) {
+ if (pos + cis[pos + 1] >= CIS_MAX_LEN)
+ goto cis_error;
+
+ switch (cis[pos]) {
+ case CISTPL_CONFIG:
+ if (cis[pos + 1] < 1)
+ goto cis_error;
+ rmsz = (cis[pos + 2] & 0x3c) >> 2;
+ rasz = cis[pos + 2] & 0x03;
+ if (4 + rasz + rmsz > cis[pos + 1])
+ goto cis_error;
+ *cor_index = cis[pos + 3] & 0x3F;
+ *cor_offset = 0;
+ for (i = 0; i <= rasz; i++)
+ *cor_offset += cis[pos + 4 + i] << (8 * i);
+ printk(KERN_DEBUG "%s: cor_index=0x%x "
+ "cor_offset=0x%x\n", dev_info,
+ *cor_index, *cor_offset);
+ if (*cor_offset > attr_len) {
+ printk(KERN_ERR "%s: COR offset not within "
+ "attr_mem\n", dev_info);
+ kfree(cis);
+ return -1;
+ }
+ break;
+
+ case CISTPL_MANFID:
+ if (cis[pos + 1] < 4)
+ goto cis_error;
+ manfid1 = cis[pos + 2] + (cis[pos + 3] << 8);
+ manfid2 = cis[pos + 4] + (cis[pos + 5] << 8);
+ printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n",
+ dev_info, manfid1, manfid2);
+ break;
+ }
+
+ pos += cis[pos + 1] + 2;
+ }
+
+ if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END)
+ goto cis_error;
+
+ for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++)
+ if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) {
+ kfree(cis);
+ return 0;
+ }
+
+ printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is"
+ " not supported card\n", dev_info, manfid1, manfid2);
+ goto fail;
+
+ cis_error:
+ printk(KERN_WARNING "%s: invalid CIS data\n", dev_info);
+
+ fail:
+ kfree(cis);
+ if (ignore_cis) {
+ printk(KERN_INFO "%s: ignore_cis parameter set - ignoring "
+ "errors during CIS verification\n", dev_info);
+ return 0;
+ }
+ return -1;
+}
+
+
+static int prism2_plx_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ unsigned int pccard_ioaddr, plx_ioaddr;
+ unsigned long pccard_attr_mem;
+ unsigned int pccard_attr_len;
+ void __iomem *attr_mem = NULL;
+ unsigned int cor_offset, cor_index;
+ u32 reg;
+ local_info_t *local = NULL;
+ struct net_device *dev = NULL;
+ struct hostap_interface *iface;
+ static int cards_found /* = 0 */;
+ int irq_registered = 0;
+ int tmd7160;
+ struct hostap_plx_priv *hw_priv;
+
+ hw_priv = kmalloc(sizeof(*hw_priv), GFP_KERNEL);
+ if (hw_priv == NULL)
+ return -ENOMEM;
+ memset(hw_priv, 0, sizeof(*hw_priv));
+
+ if (pci_enable_device(pdev))
+ return -EIO;
+
+ /* National Datacomm NCP130 based on TMD7160, not PLX9052. */
+ tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
+
+ plx_ioaddr = pci_resource_start(pdev, 1);
+ pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
+
+ if (tmd7160) {
+ /* TMD7160 */
+ attr_mem = NULL; /* no access to PC Card attribute memory */
+
+ printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
+ "irq=%d, pccard_io=0x%x\n",
+ plx_ioaddr, pdev->irq, pccard_ioaddr);
+
+ cor_offset = plx_ioaddr;
+ cor_index = 0x04;
+
+ outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
+ mdelay(1);
+ reg = inb(plx_ioaddr);
+ if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
+ printk(KERN_ERR "%s: Error setting COR (expected="
+ "0x%02x, was=0x%02x)\n", dev_info,
+ cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
+ goto fail;
+ }
+ } else {
+ /* PLX9052 */
+ pccard_attr_mem = pci_resource_start(pdev, 2);
+ pccard_attr_len = pci_resource_len(pdev, 2);
+ if (pccard_attr_len < PLX_MIN_ATTR_LEN)
+ goto fail;
+
+
+ attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
+ if (attr_mem == NULL) {
+ printk(KERN_ERR "%s: cannot remap attr_mem\n",
+ dev_info);
+ goto fail;
+ }
+
+ printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
+ "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
+ pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
+
+ if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
+ &cor_offset, &cor_index)) {
+ printk(KERN_INFO "Unknown PC Card CIS - not a "
+ "Prism2/2.5 card?\n");
+ goto fail;
+ }
+
+ printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
+ "adapter\n");
+
+ /* Write COR to enable PC Card */
+ writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
+ attr_mem + cor_offset);
+
+ /* Enable PCI interrupts if they are not already enabled */
+ reg = inl(plx_ioaddr + PLX_INTCSR);
+ printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
+ if (!(reg & PLX_INTCSR_PCI_INTEN)) {
+ outl(reg | PLX_INTCSR_PCI_INTEN,
+ plx_ioaddr + PLX_INTCSR);
+ if (!(inl(plx_ioaddr + PLX_INTCSR) &
+ PLX_INTCSR_PCI_INTEN)) {
+ printk(KERN_WARNING "%s: Could not enable "
+ "Local Interrupts\n", dev_info);
+ goto fail;
+ }
+ }
+
+ reg = inl(plx_ioaddr + PLX_CNTRL);
+ printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
+ "present=%d)\n",
+ reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
+ /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is
+ * not present; but are there really such cards in use(?) */
+ }
+
+ dev = prism2_init_local_data(&prism2_plx_funcs, cards_found,
+ &pdev->dev);
+ if (dev == NULL)
+ goto fail;
+ iface = netdev_priv(dev);
+ local = iface->local;
+ local->hw_priv = hw_priv;
+ cards_found++;
+
+ dev->irq = pdev->irq;
+ dev->base_addr = pccard_ioaddr;
+ hw_priv->attr_mem = attr_mem;
+ hw_priv->cor_offset = cor_offset;
+
+ pci_set_drvdata(pdev, dev);
+
+ if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name,
+ dev)) {
+ printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
+ goto fail;
+ } else
+ irq_registered = 1;
+
+ if (prism2_hw_config(dev, 1)) {
+ printk(KERN_DEBUG "%s: hardware initialization failed\n",
+ dev_info);
+ goto fail;
+ }
+
+ return hostap_hw_ready(dev);
+
+ fail:
+ kfree(hw_priv);
+ if (local)
+ local->hw_priv = NULL;
+ prism2_free_local_data(dev);
+
+ if (irq_registered && dev)
+ free_irq(dev->irq, dev);
+
+ if (attr_mem)
+ iounmap(attr_mem);
+
+ pci_disable_device(pdev);
+
+ return -ENODEV;
+}
+
+
+static void prism2_plx_remove(struct pci_dev *pdev)
+{
+ struct net_device *dev;
+ struct hostap_interface *iface;
+ struct hostap_plx_priv *hw_priv;
+
+ dev = pci_get_drvdata(pdev);
+ iface = netdev_priv(dev);
+ hw_priv = iface->local->hw_priv;
+
+ /* Reset the hardware, and ensure interrupts are disabled. */
+ prism2_plx_cor_sreset(iface->local);
+ hfa384x_disable_interrupts(dev);
+
+ if (hw_priv->attr_mem)
+ iounmap(hw_priv->attr_mem);
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+
+ kfree(iface->local->hw_priv);
+ iface->local->hw_priv = NULL;
+ prism2_free_local_data(dev);
+ pci_disable_device(pdev);
+}
+
+
+MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
+
+static struct pci_driver prism2_plx_drv_id = {
+ .name = "prism2_plx",
+ .id_table = prism2_plx_id_table,
+ .probe = prism2_plx_probe,
+ .remove = prism2_plx_remove,
+ .suspend = NULL,
+ .resume = NULL,
+ .enable_wake = NULL
+};
+
+
+static int __init init_prism2_plx(void)
+{
+ printk(KERN_INFO "%s: %s\n", dev_info, version);
+
+ return pci_register_driver(&prism2_plx_drv_id);
+}
+
+
+static void __exit exit_prism2_plx(void)
+{
+ pci_unregister_driver(&prism2_plx_drv_id);
+ printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
+}
+
+
+module_init(init_prism2_plx);
+module_exit(exit_prism2_plx);
diff --git a/drivers/net/wireless/hostap/hostap_proc.c b/drivers/net/wireless/hostap/hostap_proc.c
new file mode 100644
index 000000000000..a0a4cbd4937a
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_proc.c
@@ -0,0 +1,448 @@
+/* /proc routines for Host AP driver */
+
+#define PROC_LIMIT (PAGE_SIZE - 80)
+
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+static int prism2_debug_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ local_info_t *local = (local_info_t *) data;
+ int i;
+
+ if (off != 0) {
+ *eof = 1;
+ return 0;
+ }
+
+ p += sprintf(p, "next_txfid=%d next_alloc=%d\n",
+ local->next_txfid, local->next_alloc);
+ for (i = 0; i < PRISM2_TXFID_COUNT; i++)
+ p += sprintf(p, "FID: tx=%04X intransmit=%04X\n",
+ local->txfid[i], local->intransmitfid[i]);
+ p += sprintf(p, "FW TX rate control: %d\n", local->fw_tx_rate_control);
+ p += sprintf(p, "beacon_int=%d\n", local->beacon_int);
+ p += sprintf(p, "dtim_period=%d\n", local->dtim_period);
+ p += sprintf(p, "wds_max_connections=%d\n",
+ local->wds_max_connections);
+ p += sprintf(p, "dev_enabled=%d\n", local->dev_enabled);
+ p += sprintf(p, "sw_tick_stuck=%d\n", local->sw_tick_stuck);
+ for (i = 0; i < WEP_KEYS; i++) {
+ if (local->crypt[i] && local->crypt[i]->ops) {
+ p += sprintf(p, "crypt[%d]=%s\n",
+ i, local->crypt[i]->ops->name);
+ }
+ }
+ p += sprintf(p, "pri_only=%d\n", local->pri_only);
+ p += sprintf(p, "pci=%d\n", local->func->hw_type == HOSTAP_HW_PCI);
+ p += sprintf(p, "sram_type=%d\n", local->sram_type);
+ p += sprintf(p, "no_pri=%d\n", local->no_pri);
+
+ return (p - page);
+}
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+
+static int prism2_stats_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ local_info_t *local = (local_info_t *) data;
+ struct comm_tallies_sums *sums = (struct comm_tallies_sums *)
+ &local->comm_tallies;
+
+ if (off != 0) {
+ *eof = 1;
+ return 0;
+ }
+
+ p += sprintf(p, "TxUnicastFrames=%u\n", sums->tx_unicast_frames);
+ p += sprintf(p, "TxMulticastframes=%u\n", sums->tx_multicast_frames);
+ p += sprintf(p, "TxFragments=%u\n", sums->tx_fragments);
+ p += sprintf(p, "TxUnicastOctets=%u\n", sums->tx_unicast_octets);
+ p += sprintf(p, "TxMulticastOctets=%u\n", sums->tx_multicast_octets);
+ p += sprintf(p, "TxDeferredTransmissions=%u\n",
+ sums->tx_deferred_transmissions);
+ p += sprintf(p, "TxSingleRetryFrames=%u\n",
+ sums->tx_single_retry_frames);
+ p += sprintf(p, "TxMultipleRetryFrames=%u\n",
+ sums->tx_multiple_retry_frames);
+ p += sprintf(p, "TxRetryLimitExceeded=%u\n",
+ sums->tx_retry_limit_exceeded);
+ p += sprintf(p, "TxDiscards=%u\n", sums->tx_discards);
+ p += sprintf(p, "RxUnicastFrames=%u\n", sums->rx_unicast_frames);
+ p += sprintf(p, "RxMulticastFrames=%u\n", sums->rx_multicast_frames);
+ p += sprintf(p, "RxFragments=%u\n", sums->rx_fragments);
+ p += sprintf(p, "RxUnicastOctets=%u\n", sums->rx_unicast_octets);
+ p += sprintf(p, "RxMulticastOctets=%u\n", sums->rx_multicast_octets);
+ p += sprintf(p, "RxFCSErrors=%u\n", sums->rx_fcs_errors);
+ p += sprintf(p, "RxDiscardsNoBuffer=%u\n",
+ sums->rx_discards_no_buffer);
+ p += sprintf(p, "TxDiscardsWrongSA=%u\n", sums->tx_discards_wrong_sa);
+ p += sprintf(p, "RxDiscardsWEPUndecryptable=%u\n",
+ sums->rx_discards_wep_undecryptable);
+ p += sprintf(p, "RxMessageInMsgFragments=%u\n",
+ sums->rx_message_in_msg_fragments);
+ p += sprintf(p, "RxMessageInBadMsgFragments=%u\n",
+ sums->rx_message_in_bad_msg_fragments);
+ /* FIX: this may grow too long for one page(?) */
+
+ return (p - page);
+}
+
+
+static int prism2_wds_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ local_info_t *local = (local_info_t *) data;
+ struct list_head *ptr;
+ struct hostap_interface *iface;
+
+ if (off > PROC_LIMIT) {
+ *eof = 1;
+ return 0;
+ }
+
+ read_lock_bh(&local->iface_lock);
+ list_for_each(ptr, &local->hostap_interfaces) {
+ iface = list_entry(ptr, struct hostap_interface, list);
+ if (iface->type != HOSTAP_INTERFACE_WDS)
+ continue;
+ p += sprintf(p, "%s\t" MACSTR "\n",
+ iface->dev->name,
+ MAC2STR(iface->u.wds.remote_addr));
+ if ((p - page) > PROC_LIMIT) {
+ printk(KERN_DEBUG "%s: wds proc did not fit\n",
+ local->dev->name);
+ break;
+ }
+ }
+ read_unlock_bh(&local->iface_lock);
+
+ if ((p - page) <= off) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+
+ return (p - page - off);
+}
+
+
+static int prism2_bss_list_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ local_info_t *local = (local_info_t *) data;
+ struct list_head *ptr;
+ struct hostap_bss_info *bss;
+ int i;
+
+ if (off > PROC_LIMIT) {
+ *eof = 1;
+ return 0;
+ }
+
+ p += sprintf(p, "#BSSID\tlast_update\tcount\tcapab_info\tSSID(txt)\t"
+ "SSID(hex)\tWPA IE\n");
+ spin_lock_bh(&local->lock);
+ list_for_each(ptr, &local->bss_list) {
+ bss = list_entry(ptr, struct hostap_bss_info, list);
+ p += sprintf(p, MACSTR "\t%lu\t%u\t0x%x\t",
+ MAC2STR(bss->bssid), bss->last_update,
+ bss->count, bss->capab_info);
+ for (i = 0; i < bss->ssid_len; i++) {
+ p += sprintf(p, "%c",
+ bss->ssid[i] >= 32 && bss->ssid[i] < 127 ?
+ bss->ssid[i] : '_');
+ }
+ p += sprintf(p, "\t");
+ for (i = 0; i < bss->ssid_len; i++) {
+ p += sprintf(p, "%02x", bss->ssid[i]);
+ }
+ p += sprintf(p, "\t");
+ for (i = 0; i < bss->wpa_ie_len; i++) {
+ p += sprintf(p, "%02x", bss->wpa_ie[i]);
+ }
+ p += sprintf(p, "\n");
+ if ((p - page) > PROC_LIMIT) {
+ printk(KERN_DEBUG "%s: BSS proc did not fit\n",
+ local->dev->name);
+ break;
+ }
+ }
+ spin_unlock_bh(&local->lock);
+
+ if ((p - page) <= off) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+
+ return (p - page - off);
+}
+
+
+static int prism2_crypt_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ local_info_t *local = (local_info_t *) data;
+ int i;
+
+ if (off > PROC_LIMIT) {
+ *eof = 1;
+ return 0;
+ }
+
+ p += sprintf(p, "tx_keyidx=%d\n", local->tx_keyidx);
+ for (i = 0; i < WEP_KEYS; i++) {
+ if (local->crypt[i] && local->crypt[i]->ops &&
+ local->crypt[i]->ops->print_stats) {
+ p = local->crypt[i]->ops->print_stats(
+ p, local->crypt[i]->priv);
+ }
+ }
+
+ if ((p - page) <= off) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+
+ return (p - page - off);
+}
+
+
+static int prism2_pda_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ local_info_t *local = (local_info_t *) data;
+
+ if (local->pda == NULL || off >= PRISM2_PDA_SIZE) {
+ *eof = 1;
+ return 0;
+ }
+
+ if (off + count > PRISM2_PDA_SIZE)
+ count = PRISM2_PDA_SIZE - off;
+
+ memcpy(page, local->pda + off, count);
+ return count;
+}
+
+
+static int prism2_aux_dump_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ local_info_t *local = (local_info_t *) data;
+
+ if (local->func->read_aux == NULL) {
+ *eof = 1;
+ return 0;
+ }
+
+ if (local->func->read_aux(local->dev, off, count, page)) {
+ *eof = 1;
+ return 0;
+ }
+ *start = page;
+
+ return count;
+}
+
+
+#ifdef PRISM2_IO_DEBUG
+static int prism2_io_debug_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ local_info_t *local = (local_info_t *) data;
+ int head = local->io_debug_head;
+ int start_bytes, left, copy, copied;
+
+ if (off + count > PRISM2_IO_DEBUG_SIZE * 4) {
+ *eof = 1;
+ if (off >= PRISM2_IO_DEBUG_SIZE * 4)
+ return 0;
+ count = PRISM2_IO_DEBUG_SIZE * 4 - off;
+ }
+
+ copied = 0;
+ start_bytes = (PRISM2_IO_DEBUG_SIZE - head) * 4;
+ left = count;
+
+ if (off < start_bytes) {
+ copy = start_bytes - off;
+ if (copy > count)
+ copy = count;
+ memcpy(page, ((u8 *) &local->io_debug[head]) + off, copy);
+ left -= copy;
+ if (left > 0)
+ memcpy(&page[copy], local->io_debug, left);
+ } else {
+ memcpy(page, ((u8 *) local->io_debug) + (off - start_bytes),
+ left);
+ }
+
+ *start = page;
+
+ return count;
+}
+#endif /* PRISM2_IO_DEBUG */
+
+
+#ifndef PRISM2_NO_STATION_MODES
+static int prism2_scan_results_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ local_info_t *local = (local_info_t *) data;
+ int entry, i, len, total = 0;
+ struct hfa384x_hostscan_result *scanres;
+ u8 *pos;
+
+ p += sprintf(p, "CHID ANL SL BcnInt Capab Rate BSSID ATIM SupRates "
+ "SSID\n");
+
+ spin_lock_bh(&local->lock);
+ for (entry = 0; entry < local->last_scan_results_count; entry++) {
+ scanres = &local->last_scan_results[entry];
+
+ if (total + (p - page) <= off) {
+ total += p - page;
+ p = page;
+ }
+ if (total + (p - page) > off + count)
+ break;
+ if ((p - page) > (PAGE_SIZE - 200))
+ break;
+
+ p += sprintf(p, "%d %d %d %d 0x%02x %d " MACSTR " %d ",
+ le16_to_cpu(scanres->chid),
+ (s16) le16_to_cpu(scanres->anl),
+ (s16) le16_to_cpu(scanres->sl),
+ le16_to_cpu(scanres->beacon_interval),
+ le16_to_cpu(scanres->capability),
+ le16_to_cpu(scanres->rate),
+ MAC2STR(scanres->bssid),
+ le16_to_cpu(scanres->atim));
+
+ pos = scanres->sup_rates;
+ for (i = 0; i < sizeof(scanres->sup_rates); i++) {
+ if (pos[i] == 0)
+ break;
+ p += sprintf(p, "<%02x>", pos[i]);
+ }
+ p += sprintf(p, " ");
+
+ pos = scanres->ssid;
+ len = le16_to_cpu(scanres->ssid_len);
+ if (len > 32)
+ len = 32;
+ for (i = 0; i < len; i++) {
+ unsigned char c = pos[i];
+ if (c >= 32 && c < 127)
+ p += sprintf(p, "%c", c);
+ else
+ p += sprintf(p, "<%02x>", c);
+ }
+ p += sprintf(p, "\n");
+ }
+ spin_unlock_bh(&local->lock);
+
+ total += (p - page);
+ if (total >= off + count)
+ *eof = 1;
+
+ if (total < off) {
+ *eof = 1;
+ return 0;
+ }
+
+ len = total - off;
+ if (len > (p - page))
+ len = p - page;
+ *start = p - len;
+ if (len > count)
+ len = count;
+
+ return len;
+}
+#endif /* PRISM2_NO_STATION_MODES */
+
+
+void hostap_init_proc(local_info_t *local)
+{
+ local->proc = NULL;
+
+ if (hostap_proc == NULL) {
+ printk(KERN_WARNING "%s: hostap proc directory not created\n",
+ local->dev->name);
+ return;
+ }
+
+ local->proc = proc_mkdir(local->ddev->name, hostap_proc);
+ if (local->proc == NULL) {
+ printk(KERN_INFO "/proc/net/hostap/%s creation failed\n",
+ local->ddev->name);
+ return;
+ }
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+ create_proc_read_entry("debug", 0, local->proc,
+ prism2_debug_proc_read, local);
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+ create_proc_read_entry("stats", 0, local->proc,
+ prism2_stats_proc_read, local);
+ create_proc_read_entry("wds", 0, local->proc,
+ prism2_wds_proc_read, local);
+ create_proc_read_entry("pda", 0, local->proc,
+ prism2_pda_proc_read, local);
+ create_proc_read_entry("aux_dump", 0, local->proc,
+ prism2_aux_dump_proc_read, local);
+ create_proc_read_entry("bss_list", 0, local->proc,
+ prism2_bss_list_proc_read, local);
+ create_proc_read_entry("crypt", 0, local->proc,
+ prism2_crypt_proc_read, local);
+#ifdef PRISM2_IO_DEBUG
+ create_proc_read_entry("io_debug", 0, local->proc,
+ prism2_io_debug_proc_read, local);
+#endif /* PRISM2_IO_DEBUG */
+#ifndef PRISM2_NO_STATION_MODES
+ create_proc_read_entry("scan_results", 0, local->proc,
+ prism2_scan_results_proc_read, local);
+#endif /* PRISM2_NO_STATION_MODES */
+}
+
+
+void hostap_remove_proc(local_info_t *local)
+{
+ if (local->proc != NULL) {
+#ifndef PRISM2_NO_STATION_MODES
+ remove_proc_entry("scan_results", local->proc);
+#endif /* PRISM2_NO_STATION_MODES */
+#ifdef PRISM2_IO_DEBUG
+ remove_proc_entry("io_debug", local->proc);
+#endif /* PRISM2_IO_DEBUG */
+ remove_proc_entry("pda", local->proc);
+ remove_proc_entry("aux_dump", local->proc);
+ remove_proc_entry("wds", local->proc);
+ remove_proc_entry("stats", local->proc);
+ remove_proc_entry("bss_list", local->proc);
+ remove_proc_entry("crypt", local->proc);
+#ifndef PRISM2_NO_PROCFS_DEBUG
+ remove_proc_entry("debug", local->proc);
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+ if (hostap_proc != NULL)
+ remove_proc_entry(local->proc->name, hostap_proc);
+ }
+}
+
+
+EXPORT_SYMBOL(hostap_init_proc);
+EXPORT_SYMBOL(hostap_remove_proc);
diff --git a/drivers/net/wireless/hostap/hostap_wlan.h b/drivers/net/wireless/hostap/hostap_wlan.h
new file mode 100644
index 000000000000..cc061e1560d3
--- /dev/null
+++ b/drivers/net/wireless/hostap/hostap_wlan.h
@@ -0,0 +1,1033 @@
+#ifndef HOSTAP_WLAN_H
+#define HOSTAP_WLAN_H
+
+#include "hostap_config.h"
+#include "hostap_common.h"
+
+#define MAX_PARM_DEVICES 8
+#define PARM_MIN_MAX "1-" __MODULE_STRING(MAX_PARM_DEVICES)
+#define DEF_INTS -1, -1, -1, -1, -1, -1, -1
+#define GET_INT_PARM(var,idx) var[var[idx] < 0 ? 0 : idx]
+
+
+/* Specific skb->protocol value that indicates that the packet already contains
+ * txdesc header.
+ * FIX: This might need own value that would be allocated especially for Prism2
+ * txdesc; ETH_P_CONTROL is commented as "Card specific control frames".
+ * However, these skb's should have only minimal path in the kernel side since
+ * prism2_send_mgmt() sends these with dev_queue_xmit() to prism2_tx(). */
+#define ETH_P_HOSTAP ETH_P_CONTROL
+
+/* ARPHRD_IEEE80211_PRISM uses a bloated version of Prism2 RX frame header
+ * (from linux-wlan-ng) */
+struct linux_wlan_ng_val {
+ u32 did;
+ u16 status, len;
+ u32 data;
+} __attribute__ ((packed));
+
+struct linux_wlan_ng_prism_hdr {
+ u32 msgcode, msglen;
+ char devname[16];
+ struct linux_wlan_ng_val hosttime, mactime, channel, rssi, sq, signal,
+ noise, rate, istx, frmlen;
+} __attribute__ ((packed));
+
+struct linux_wlan_ng_cap_hdr {
+ u32 version;
+ u32 length;
+ u64 mactime;
+ u64 hosttime;
+ u32 phytype;
+ u32 channel;
+ u32 datarate;
+ u32 antenna;
+ u32 priority;
+ u32 ssi_type;
+ s32 ssi_signal;
+ s32 ssi_noise;
+ u32 preamble;
+ u32 encoding;
+} __attribute__ ((packed));
+
+#define LWNG_CAP_DID_BASE (4 | (1 << 6)) /* section 4, group 1 */
+#define LWNG_CAPHDR_VERSION 0x80211001
+
+struct hfa384x_rx_frame {
+ /* HFA384X RX frame descriptor */
+ u16 status; /* HFA384X_RX_STATUS_ flags */
+ u32 time; /* timestamp, 1 microsecond resolution */
+ u8 silence; /* 27 .. 154; seems to be 0 */
+ u8 signal; /* 27 .. 154 */
+ u8 rate; /* 10, 20, 55, or 110 */
+ u8 rxflow;
+ u32 reserved;
+
+ /* 802.11 */
+ u16 frame_control;
+ u16 duration_id;
+ u8 addr1[6];
+ u8 addr2[6];
+ u8 addr3[6];
+ u16 seq_ctrl;
+ u8 addr4[6];
+ u16 data_len;
+
+ /* 802.3 */
+ u8 dst_addr[6];
+ u8 src_addr[6];
+ u16 len;
+
+ /* followed by frame data; max 2304 bytes */
+} __attribute__ ((packed));
+
+
+struct hfa384x_tx_frame {
+ /* HFA384X TX frame descriptor */
+ u16 status; /* HFA384X_TX_STATUS_ flags */
+ u16 reserved1;
+ u16 reserved2;
+ u32 sw_support;
+ u8 retry_count; /* not yet implemented */
+ u8 tx_rate; /* Host AP only; 0 = firmware, or 10, 20, 55, 110 */
+ u16 tx_control; /* HFA384X_TX_CTRL_ flags */
+
+ /* 802.11 */
+ u16 frame_control; /* parts not used */
+ u16 duration_id;
+ u8 addr1[6];
+ u8 addr2[6]; /* filled by firmware */
+ u8 addr3[6];
+ u16 seq_ctrl; /* filled by firmware */
+ u8 addr4[6];
+ u16 data_len;
+
+ /* 802.3 */
+ u8 dst_addr[6];
+ u8 src_addr[6];
+ u16 len;
+
+ /* followed by frame data; max 2304 bytes */
+} __attribute__ ((packed));
+
+
+struct hfa384x_rid_hdr
+{
+ u16 len;
+ u16 rid;
+} __attribute__ ((packed));
+
+
+/* Macro for converting signal levels (range 27 .. 154) to wireless ext
+ * dBm value with some accuracy */
+#define HFA384X_LEVEL_TO_dBm(v) 0x100 + (v) * 100 / 255 - 100
+
+#define HFA384X_LEVEL_TO_dBm_sign(v) (v) * 100 / 255 - 100
+
+struct hfa384x_scan_request {
+ u16 channel_list;
+ u16 txrate; /* HFA384X_RATES_* */
+} __attribute__ ((packed));
+
+struct hfa384x_hostscan_request {
+ u16 channel_list;
+ u16 txrate;
+ u16 target_ssid_len;
+ u8 target_ssid[32];
+} __attribute__ ((packed));
+
+struct hfa384x_join_request {
+ u8 bssid[6];
+ u16 channel;
+} __attribute__ ((packed));
+
+struct hfa384x_info_frame {
+ u16 len;
+ u16 type;
+} __attribute__ ((packed));
+
+struct hfa384x_comm_tallies {
+ u16 tx_unicast_frames;
+ u16 tx_multicast_frames;
+ u16 tx_fragments;
+ u16 tx_unicast_octets;
+ u16 tx_multicast_octets;
+ u16 tx_deferred_transmissions;
+ u16 tx_single_retry_frames;
+ u16 tx_multiple_retry_frames;
+ u16 tx_retry_limit_exceeded;
+ u16 tx_discards;
+ u16 rx_unicast_frames;
+ u16 rx_multicast_frames;
+ u16 rx_fragments;
+ u16 rx_unicast_octets;
+ u16 rx_multicast_octets;
+ u16 rx_fcs_errors;
+ u16 rx_discards_no_buffer;
+ u16 tx_discards_wrong_sa;
+ u16 rx_discards_wep_undecryptable;
+ u16 rx_message_in_msg_fragments;
+ u16 rx_message_in_bad_msg_fragments;
+} __attribute__ ((packed));
+
+struct hfa384x_comm_tallies32 {
+ u32 tx_unicast_frames;
+ u32 tx_multicast_frames;
+ u32 tx_fragments;
+ u32 tx_unicast_octets;
+ u32 tx_multicast_octets;
+ u32 tx_deferred_transmissions;
+ u32 tx_single_retry_frames;
+ u32 tx_multiple_retry_frames;
+ u32 tx_retry_limit_exceeded;
+ u32 tx_discards;
+ u32 rx_unicast_frames;
+ u32 rx_multicast_frames;
+ u32 rx_fragments;
+ u32 rx_unicast_octets;
+ u32 rx_multicast_octets;
+ u32 rx_fcs_errors;
+ u32 rx_discards_no_buffer;
+ u32 tx_discards_wrong_sa;
+ u32 rx_discards_wep_undecryptable;
+ u32 rx_message_in_msg_fragments;
+ u32 rx_message_in_bad_msg_fragments;
+} __attribute__ ((packed));
+
+struct hfa384x_scan_result_hdr {
+ u16 reserved;
+ u16 scan_reason;
+#define HFA384X_SCAN_IN_PROGRESS 0 /* no results available yet */
+#define HFA384X_SCAN_HOST_INITIATED 1
+#define HFA384X_SCAN_FIRMWARE_INITIATED 2
+#define HFA384X_SCAN_INQUIRY_FROM_HOST 3
+} __attribute__ ((packed));
+
+#define HFA384X_SCAN_MAX_RESULTS 32
+
+struct hfa384x_scan_result {
+ u16 chid;
+ u16 anl;
+ u16 sl;
+ u8 bssid[6];
+ u16 beacon_interval;
+ u16 capability;
+ u16 ssid_len;
+ u8 ssid[32];
+ u8 sup_rates[10];
+ u16 rate;
+} __attribute__ ((packed));
+
+struct hfa384x_hostscan_result {
+ u16 chid;
+ u16 anl;
+ u16 sl;
+ u8 bssid[6];
+ u16 beacon_interval;
+ u16 capability;
+ u16 ssid_len;
+ u8 ssid[32];
+ u8 sup_rates[10];
+ u16 rate;
+ u16 atim;
+} __attribute__ ((packed));
+
+struct comm_tallies_sums {
+ unsigned int tx_unicast_frames;
+ unsigned int tx_multicast_frames;
+ unsigned int tx_fragments;
+ unsigned int tx_unicast_octets;
+ unsigned int tx_multicast_octets;
+ unsigned int tx_deferred_transmissions;
+ unsigned int tx_single_retry_frames;
+ unsigned int tx_multiple_retry_frames;
+ unsigned int tx_retry_limit_exceeded;
+ unsigned int tx_discards;
+ unsigned int rx_unicast_frames;
+ unsigned int rx_multicast_frames;
+ unsigned int rx_fragments;
+ unsigned int rx_unicast_octets;
+ unsigned int rx_multicast_octets;
+ unsigned int rx_fcs_errors;
+ unsigned int rx_discards_no_buffer;
+ unsigned int tx_discards_wrong_sa;
+ unsigned int rx_discards_wep_undecryptable;
+ unsigned int rx_message_in_msg_fragments;
+ unsigned int rx_message_in_bad_msg_fragments;
+};
+
+
+struct hfa384x_regs {
+ u16 cmd;
+ u16 evstat;
+ u16 offset0;
+ u16 offset1;
+ u16 swsupport0;
+};
+
+
+#if defined(PRISM2_PCCARD) || defined(PRISM2_PLX)
+/* I/O ports for HFA384X Controller access */
+#define HFA384X_CMD_OFF 0x00
+#define HFA384X_PARAM0_OFF 0x02
+#define HFA384X_PARAM1_OFF 0x04
+#define HFA384X_PARAM2_OFF 0x06
+#define HFA384X_STATUS_OFF 0x08
+#define HFA384X_RESP0_OFF 0x0A
+#define HFA384X_RESP1_OFF 0x0C
+#define HFA384X_RESP2_OFF 0x0E
+#define HFA384X_INFOFID_OFF 0x10
+#define HFA384X_CONTROL_OFF 0x14
+#define HFA384X_SELECT0_OFF 0x18
+#define HFA384X_SELECT1_OFF 0x1A
+#define HFA384X_OFFSET0_OFF 0x1C
+#define HFA384X_OFFSET1_OFF 0x1E
+#define HFA384X_RXFID_OFF 0x20
+#define HFA384X_ALLOCFID_OFF 0x22
+#define HFA384X_TXCOMPLFID_OFF 0x24
+#define HFA384X_SWSUPPORT0_OFF 0x28
+#define HFA384X_SWSUPPORT1_OFF 0x2A
+#define HFA384X_SWSUPPORT2_OFF 0x2C
+#define HFA384X_EVSTAT_OFF 0x30
+#define HFA384X_INTEN_OFF 0x32
+#define HFA384X_EVACK_OFF 0x34
+#define HFA384X_DATA0_OFF 0x36
+#define HFA384X_DATA1_OFF 0x38
+#define HFA384X_AUXPAGE_OFF 0x3A
+#define HFA384X_AUXOFFSET_OFF 0x3C
+#define HFA384X_AUXDATA_OFF 0x3E
+#endif /* PRISM2_PCCARD || PRISM2_PLX */
+
+#ifdef PRISM2_PCI
+/* Memory addresses for ISL3874 controller access */
+#define HFA384X_CMD_OFF 0x00
+#define HFA384X_PARAM0_OFF 0x04
+#define HFA384X_PARAM1_OFF 0x08
+#define HFA384X_PARAM2_OFF 0x0C
+#define HFA384X_STATUS_OFF 0x10
+#define HFA384X_RESP0_OFF 0x14
+#define HFA384X_RESP1_OFF 0x18
+#define HFA384X_RESP2_OFF 0x1C
+#define HFA384X_INFOFID_OFF 0x20
+#define HFA384X_CONTROL_OFF 0x28
+#define HFA384X_SELECT0_OFF 0x30
+#define HFA384X_SELECT1_OFF 0x34
+#define HFA384X_OFFSET0_OFF 0x38
+#define HFA384X_OFFSET1_OFF 0x3C
+#define HFA384X_RXFID_OFF 0x40
+#define HFA384X_ALLOCFID_OFF 0x44
+#define HFA384X_TXCOMPLFID_OFF 0x48
+#define HFA384X_PCICOR_OFF 0x4C
+#define HFA384X_SWSUPPORT0_OFF 0x50
+#define HFA384X_SWSUPPORT1_OFF 0x54
+#define HFA384X_SWSUPPORT2_OFF 0x58
+#define HFA384X_PCIHCR_OFF 0x5C
+#define HFA384X_EVSTAT_OFF 0x60
+#define HFA384X_INTEN_OFF 0x64
+#define HFA384X_EVACK_OFF 0x68
+#define HFA384X_DATA0_OFF 0x6C
+#define HFA384X_DATA1_OFF 0x70
+#define HFA384X_AUXPAGE_OFF 0x74
+#define HFA384X_AUXOFFSET_OFF 0x78
+#define HFA384X_AUXDATA_OFF 0x7C
+#define HFA384X_PCI_M0_ADDRH_OFF 0x80
+#define HFA384X_PCI_M0_ADDRL_OFF 0x84
+#define HFA384X_PCI_M0_LEN_OFF 0x88
+#define HFA384X_PCI_M0_CTL_OFF 0x8C
+#define HFA384X_PCI_STATUS_OFF 0x98
+#define HFA384X_PCI_M1_ADDRH_OFF 0xA0
+#define HFA384X_PCI_M1_ADDRL_OFF 0xA4
+#define HFA384X_PCI_M1_LEN_OFF 0xA8
+#define HFA384X_PCI_M1_CTL_OFF 0xAC
+
+/* PCI bus master control bits (these are undocumented; based on guessing and
+ * experimenting..) */
+#define HFA384X_PCI_CTL_FROM_BAP (BIT(5) | BIT(1) | BIT(0))
+#define HFA384X_PCI_CTL_TO_BAP (BIT(5) | BIT(0))
+
+#endif /* PRISM2_PCI */
+
+
+/* Command codes for CMD reg. */
+#define HFA384X_CMDCODE_INIT 0x00
+#define HFA384X_CMDCODE_ENABLE 0x01
+#define HFA384X_CMDCODE_DISABLE 0x02
+#define HFA384X_CMDCODE_ALLOC 0x0A
+#define HFA384X_CMDCODE_TRANSMIT 0x0B
+#define HFA384X_CMDCODE_INQUIRE 0x11
+#define HFA384X_CMDCODE_ACCESS 0x21
+#define HFA384X_CMDCODE_ACCESS_WRITE (0x21 | BIT(8))
+#define HFA384X_CMDCODE_DOWNLOAD 0x22
+#define HFA384X_CMDCODE_READMIF 0x30
+#define HFA384X_CMDCODE_WRITEMIF 0x31
+#define HFA384X_CMDCODE_TEST 0x38
+
+#define HFA384X_CMDCODE_MASK 0x3F
+
+/* Test mode operations */
+#define HFA384X_TEST_CHANGE_CHANNEL 0x08
+#define HFA384X_TEST_MONITOR 0x0B
+#define HFA384X_TEST_STOP 0x0F
+#define HFA384X_TEST_CFG_BITS 0x15
+#define HFA384X_TEST_CFG_BIT_ALC BIT(3)
+
+#define HFA384X_CMD_BUSY BIT(15)
+
+#define HFA384X_CMD_TX_RECLAIM BIT(8)
+
+#define HFA384X_OFFSET_ERR BIT(14)
+#define HFA384X_OFFSET_BUSY BIT(15)
+
+
+/* ProgMode for download command */
+#define HFA384X_PROGMODE_DISABLE 0
+#define HFA384X_PROGMODE_ENABLE_VOLATILE 1
+#define HFA384X_PROGMODE_ENABLE_NON_VOLATILE 2
+#define HFA384X_PROGMODE_PROGRAM_NON_VOLATILE 3
+
+#define HFA384X_AUX_MAGIC0 0xfe01
+#define HFA384X_AUX_MAGIC1 0xdc23
+#define HFA384X_AUX_MAGIC2 0xba45
+
+#define HFA384X_AUX_PORT_DISABLED 0
+#define HFA384X_AUX_PORT_DISABLE BIT(14)
+#define HFA384X_AUX_PORT_ENABLE BIT(15)
+#define HFA384X_AUX_PORT_ENABLED (BIT(14) | BIT(15))
+#define HFA384X_AUX_PORT_MASK (BIT(14) | BIT(15))
+
+#define PRISM2_PDA_SIZE 1024
+
+
+/* Events; EvStat, Interrupt mask (IntEn), and acknowledge bits (EvAck) */
+#define HFA384X_EV_TICK BIT(15)
+#define HFA384X_EV_WTERR BIT(14)
+#define HFA384X_EV_INFDROP BIT(13)
+#ifdef PRISM2_PCI
+#define HFA384X_EV_PCI_M1 BIT(9)
+#define HFA384X_EV_PCI_M0 BIT(8)
+#endif /* PRISM2_PCI */
+#define HFA384X_EV_INFO BIT(7)
+#define HFA384X_EV_DTIM BIT(5)
+#define HFA384X_EV_CMD BIT(4)
+#define HFA384X_EV_ALLOC BIT(3)
+#define HFA384X_EV_TXEXC BIT(2)
+#define HFA384X_EV_TX BIT(1)
+#define HFA384X_EV_RX BIT(0)
+
+
+/* HFA384X Information frames */
+#define HFA384X_INFO_HANDOVERADDR 0xF000 /* AP f/w ? */
+#define HFA384X_INFO_HANDOVERDEAUTHADDR 0xF001 /* AP f/w 1.3.7 */
+#define HFA384X_INFO_COMMTALLIES 0xF100
+#define HFA384X_INFO_SCANRESULTS 0xF101
+#define HFA384X_INFO_CHANNELINFORESULTS 0xF102 /* AP f/w only */
+#define HFA384X_INFO_HOSTSCANRESULTS 0xF103
+#define HFA384X_INFO_LINKSTATUS 0xF200
+#define HFA384X_INFO_ASSOCSTATUS 0xF201 /* ? */
+#define HFA384X_INFO_AUTHREQ 0xF202 /* ? */
+#define HFA384X_INFO_PSUSERCNT 0xF203 /* ? */
+#define HFA384X_INFO_KEYIDCHANGED 0xF204 /* ? */
+
+enum { HFA384X_LINKSTATUS_CONNECTED = 1,
+ HFA384X_LINKSTATUS_DISCONNECTED = 2,
+ HFA384X_LINKSTATUS_AP_CHANGE = 3,
+ HFA384X_LINKSTATUS_AP_OUT_OF_RANGE = 4,
+ HFA384X_LINKSTATUS_AP_IN_RANGE = 5,
+ HFA384X_LINKSTATUS_ASSOC_FAILED = 6 };
+
+enum { HFA384X_PORTTYPE_BSS = 1, HFA384X_PORTTYPE_WDS = 2,
+ HFA384X_PORTTYPE_PSEUDO_IBSS = 3, HFA384X_PORTTYPE_IBSS = 0,
+ HFA384X_PORTTYPE_HOSTAP = 6 };
+
+#define HFA384X_RATES_1MBPS BIT(0)
+#define HFA384X_RATES_2MBPS BIT(1)
+#define HFA384X_RATES_5MBPS BIT(2)
+#define HFA384X_RATES_11MBPS BIT(3)
+
+#define HFA384X_ROAMING_FIRMWARE 1
+#define HFA384X_ROAMING_HOST 2
+#define HFA384X_ROAMING_DISABLED 3
+
+#define HFA384X_WEPFLAGS_PRIVACYINVOKED BIT(0)
+#define HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED BIT(1)
+#define HFA384X_WEPFLAGS_HOSTENCRYPT BIT(4)
+#define HFA384X_WEPFLAGS_HOSTDECRYPT BIT(7)
+
+#define HFA384X_RX_STATUS_MSGTYPE (BIT(15) | BIT(14) | BIT(13))
+#define HFA384X_RX_STATUS_PCF BIT(12)
+#define HFA384X_RX_STATUS_MACPORT (BIT(10) | BIT(9) | BIT(8))
+#define HFA384X_RX_STATUS_UNDECR BIT(1)
+#define HFA384X_RX_STATUS_FCSERR BIT(0)
+
+#define HFA384X_RX_STATUS_GET_MSGTYPE(s) \
+(((s) & HFA384X_RX_STATUS_MSGTYPE) >> 13)
+#define HFA384X_RX_STATUS_GET_MACPORT(s) \
+(((s) & HFA384X_RX_STATUS_MACPORT) >> 8)
+
+enum { HFA384X_RX_MSGTYPE_NORMAL = 0, HFA384X_RX_MSGTYPE_RFC1042 = 1,
+ HFA384X_RX_MSGTYPE_BRIDGETUNNEL = 2, HFA384X_RX_MSGTYPE_MGMT = 4 };
+
+
+#define HFA384X_TX_CTRL_ALT_RTRY BIT(5)
+#define HFA384X_TX_CTRL_802_11 BIT(3)
+#define HFA384X_TX_CTRL_802_3 0
+#define HFA384X_TX_CTRL_TX_EX BIT(2)
+#define HFA384X_TX_CTRL_TX_OK BIT(1)
+
+#define HFA384X_TX_STATUS_RETRYERR BIT(0)
+#define HFA384X_TX_STATUS_AGEDERR BIT(1)
+#define HFA384X_TX_STATUS_DISCON BIT(2)
+#define HFA384X_TX_STATUS_FORMERR BIT(3)
+
+/* HFA3861/3863 (BBP) Control Registers */
+#define HFA386X_CR_TX_CONFIGURE 0x12 /* CR9 */
+#define HFA386X_CR_RX_CONFIGURE 0x14 /* CR10 */
+#define HFA386X_CR_A_D_TEST_MODES2 0x1A /* CR13 */
+#define HFA386X_CR_MANUAL_TX_POWER 0x3E /* CR31 */
+#define HFA386X_CR_MEASURED_TX_POWER 0x74 /* CR58 */
+
+
+#ifdef __KERNEL__
+
+#define PRISM2_TXFID_COUNT 8
+#define PRISM2_DATA_MAXLEN 2304
+#define PRISM2_TXFID_LEN (PRISM2_DATA_MAXLEN + sizeof(struct hfa384x_tx_frame))
+#define PRISM2_TXFID_EMPTY 0xffff
+#define PRISM2_TXFID_RESERVED 0xfffe
+#define PRISM2_DUMMY_FID 0xffff
+#define MAX_SSID_LEN 32
+#define MAX_NAME_LEN 32 /* this is assumed to be equal to MAX_SSID_LEN */
+
+#define PRISM2_DUMP_RX_HDR BIT(0)
+#define PRISM2_DUMP_TX_HDR BIT(1)
+#define PRISM2_DUMP_TXEXC_HDR BIT(2)
+
+struct hostap_tx_callback_info {
+ u16 idx;
+ void (*func)(struct sk_buff *, int ok, void *);
+ void *data;
+ struct hostap_tx_callback_info *next;
+};
+
+
+/* IEEE 802.11 requires that STA supports concurrent reception of at least
+ * three fragmented frames. This define can be increased to support more
+ * concurrent frames, but it should be noted that each entry can consume about
+ * 2 kB of RAM and increasing cache size will slow down frame reassembly. */
+#define PRISM2_FRAG_CACHE_LEN 4
+
+struct prism2_frag_entry {
+ unsigned long first_frag_time;
+ unsigned int seq;
+ unsigned int last_frag;
+ struct sk_buff *skb;
+ u8 src_addr[ETH_ALEN];
+ u8 dst_addr[ETH_ALEN];
+};
+
+
+struct hostap_cmd_queue {
+ struct list_head list;
+ wait_queue_head_t compl;
+ volatile enum { CMD_SLEEP, CMD_CALLBACK, CMD_COMPLETED } type;
+ void (*callback)(struct net_device *dev, long context, u16 resp0,
+ u16 res);
+ long context;
+ u16 cmd, param0, param1;
+ u16 resp0, res;
+ volatile int issued, issuing;
+
+ atomic_t usecnt;
+ int del_req;
+};
+
+/* options for hw_shutdown */
+#define HOSTAP_HW_NO_DISABLE BIT(0)
+#define HOSTAP_HW_ENABLE_CMDCOMPL BIT(1)
+
+typedef struct local_info local_info_t;
+
+struct prism2_helper_functions {
+ /* these functions are defined in hardware model specific files
+ * (hostap_{cs,plx,pci}.c */
+ int (*card_present)(local_info_t *local);
+ void (*cor_sreset)(local_info_t *local);
+ int (*dev_open)(local_info_t *local);
+ int (*dev_close)(local_info_t *local);
+ void (*genesis_reset)(local_info_t *local, int hcr);
+
+ /* the following functions are from hostap_hw.c, but they may have some
+ * hardware model specific code */
+
+ /* FIX: low-level commands like cmd might disappear at some point to
+ * make it easier to change them if needed (e.g., cmd would be replaced
+ * with write_mif/read_mif/testcmd/inquire); at least get_rid and
+ * set_rid might move to hostap_{cs,plx,pci}.c */
+ int (*cmd)(struct net_device *dev, u16 cmd, u16 param0, u16 *param1,
+ u16 *resp0);
+ void (*read_regs)(struct net_device *dev, struct hfa384x_regs *regs);
+ int (*get_rid)(struct net_device *dev, u16 rid, void *buf, int len,
+ int exact_len);
+ int (*set_rid)(struct net_device *dev, u16 rid, void *buf, int len);
+ int (*hw_enable)(struct net_device *dev, int initial);
+ int (*hw_config)(struct net_device *dev, int initial);
+ void (*hw_reset)(struct net_device *dev);
+ void (*hw_shutdown)(struct net_device *dev, int no_disable);
+ int (*reset_port)(struct net_device *dev);
+ void (*schedule_reset)(local_info_t *local);
+ int (*download)(local_info_t *local,
+ struct prism2_download_param *param);
+ int (*tx)(struct sk_buff *skb, struct net_device *dev);
+ int (*set_tim)(struct net_device *dev, int aid, int set);
+ int (*read_aux)(struct net_device *dev, unsigned addr, int len,
+ u8 *buf);
+
+ int need_tx_headroom; /* number of bytes of headroom needed before
+ * IEEE 802.11 header */
+ enum { HOSTAP_HW_PCCARD, HOSTAP_HW_PLX, HOSTAP_HW_PCI } hw_type;
+};
+
+
+struct prism2_download_data {
+ u32 dl_cmd;
+ u32 start_addr;
+ u32 num_areas;
+ struct prism2_download_data_area {
+ u32 addr; /* wlan card address */
+ u32 len;
+ u8 *data; /* allocated data */
+ } data[0];
+};
+
+
+#define HOSTAP_MAX_BSS_COUNT 64
+#define MAX_WPA_IE_LEN 64
+
+struct hostap_bss_info {
+ struct list_head list;
+ unsigned long last_update;
+ unsigned int count;
+ u8 bssid[ETH_ALEN];
+ u16 capab_info;
+ u8 ssid[32];
+ size_t ssid_len;
+ u8 wpa_ie[MAX_WPA_IE_LEN];
+ size_t wpa_ie_len;
+ u8 rsn_ie[MAX_WPA_IE_LEN];
+ size_t rsn_ie_len;
+ int chan;
+ int included;
+};
+
+
+/* Per radio private Host AP data - shared by all net devices interfaces used
+ * by each radio (wlan#, wlan#ap, wlan#sta, WDS).
+ * ((struct hostap_interface *) netdev_priv(dev))->local points to this
+ * structure. */
+struct local_info {
+ struct module *hw_module;
+ int card_idx;
+ int dev_enabled;
+ int master_dev_auto_open; /* was master device opened automatically */
+ int num_dev_open; /* number of open devices */
+ struct net_device *dev; /* master radio device */
+ struct net_device *ddev; /* main data device */
+ struct list_head hostap_interfaces; /* Host AP interface list (contains
+ * struct hostap_interface entries)
+ */
+ rwlock_t iface_lock; /* hostap_interfaces read lock; use write lock
+ * when removing entries from the list.
+ * TX and RX paths can use read lock. */
+ spinlock_t cmdlock, baplock, lock;
+ struct semaphore rid_bap_sem;
+ u16 infofid; /* MAC buffer id for info frame */
+ /* txfid, intransmitfid, next_txtid, and next_alloc are protected by
+ * txfidlock */
+ spinlock_t txfidlock;
+ int txfid_len; /* length of allocated TX buffers */
+ u16 txfid[PRISM2_TXFID_COUNT]; /* buffer IDs for TX frames */
+ /* buffer IDs for intransmit frames or PRISM2_TXFID_EMPTY if
+ * corresponding txfid is free for next TX frame */
+ u16 intransmitfid[PRISM2_TXFID_COUNT];
+ int next_txfid; /* index to the next txfid to be checked for
+ * availability */
+ int next_alloc; /* index to the next intransmitfid to be checked for
+ * allocation events */
+
+ /* bitfield for atomic bitops */
+#define HOSTAP_BITS_TRANSMIT 0
+#define HOSTAP_BITS_BAP_TASKLET 1
+#define HOSTAP_BITS_BAP_TASKLET2 2
+ long bits;
+
+ struct ap_data *ap;
+
+ char essid[MAX_SSID_LEN + 1];
+ char name[MAX_NAME_LEN + 1];
+ int name_set;
+ u16 channel_mask; /* mask of allowed channels */
+ u16 scan_channel_mask; /* mask of channels to be scanned */
+ struct comm_tallies_sums comm_tallies;
+ struct net_device_stats stats;
+ struct proc_dir_entry *proc;
+ int iw_mode; /* operating mode (IW_MODE_*) */
+ int pseudo_adhoc; /* 0: IW_MODE_ADHOC is real 802.11 compliant IBSS
+ * 1: IW_MODE_ADHOC is "pseudo IBSS" */
+ char bssid[ETH_ALEN];
+ int channel;
+ int beacon_int;
+ int dtim_period;
+ int mtu;
+ int frame_dump; /* dump RX/TX frame headers, PRISM2_DUMP_ flags */
+ int fw_tx_rate_control;
+ u16 tx_rate_control;
+ u16 basic_rates;
+ int hw_resetting;
+ int hw_ready;
+ int hw_reset_tries; /* how many times reset has been tried */
+ int hw_downloading;
+ int shutdown;
+ int pri_only;
+ int no_pri; /* no PRI f/w present */
+ int sram_type; /* 8 = x8 SRAM, 16 = x16 SRAM, -1 = unknown */
+
+ enum {
+ PRISM2_TXPOWER_AUTO = 0, PRISM2_TXPOWER_OFF,
+ PRISM2_TXPOWER_FIXED, PRISM2_TXPOWER_UNKNOWN
+ } txpower_type;
+ int txpower; /* if txpower_type == PRISM2_TXPOWER_FIXED */
+
+ /* command queue for hfa384x_cmd(); protected with cmdlock */
+ struct list_head cmd_queue;
+ /* max_len for cmd_queue; in addition, cmd_callback can use two
+ * additional entries to prevent sleeping commands from stopping
+ * transmits */
+#define HOSTAP_CMD_QUEUE_MAX_LEN 16
+ int cmd_queue_len; /* number of entries in cmd_queue */
+
+ /* if card timeout is detected in interrupt context, reset_queue is
+ * used to schedule card reseting to be done in user context */
+ struct work_struct reset_queue;
+
+ /* For scheduling a change of the promiscuous mode RID */
+ int is_promisc;
+ struct work_struct set_multicast_list_queue;
+
+ struct work_struct set_tim_queue;
+ struct list_head set_tim_list;
+ spinlock_t set_tim_lock;
+
+ int wds_max_connections;
+ int wds_connections;
+#define HOSTAP_WDS_BROADCAST_RA BIT(0)
+#define HOSTAP_WDS_AP_CLIENT BIT(1)
+#define HOSTAP_WDS_STANDARD_FRAME BIT(2)
+ u32 wds_type;
+ u16 tx_control; /* flags to be used in TX description */
+ int manual_retry_count; /* -1 = use f/w default; otherwise retry count
+ * to be used with all frames */
+
+ struct iw_statistics wstats;
+ unsigned long scan_timestamp; /* Time started to scan */
+ enum {
+ PRISM2_MONITOR_80211 = 0, PRISM2_MONITOR_PRISM = 1,
+ PRISM2_MONITOR_CAPHDR = 2
+ } monitor_type;
+ int (*saved_eth_header_parse)(struct sk_buff *skb,
+ unsigned char *haddr);
+ int monitor_allow_fcserr;
+
+ int hostapd; /* whether user space daemon, hostapd, is used for AP
+ * management */
+ int hostapd_sta; /* whether hostapd is used with an extra STA interface
+ */
+ struct net_device *apdev;
+ struct net_device_stats apdevstats;
+
+ char assoc_ap_addr[ETH_ALEN];
+ struct net_device *stadev;
+ struct net_device_stats stadevstats;
+
+#define WEP_KEYS 4
+#define WEP_KEY_LEN 13
+ struct ieee80211_crypt_data *crypt[WEP_KEYS];
+ int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */
+ struct timer_list crypt_deinit_timer;
+ struct list_head crypt_deinit_list;
+
+ int open_wep; /* allow unencrypted frames */
+ int host_encrypt;
+ int host_decrypt;
+ int privacy_invoked; /* force privacy invoked flag even if no keys are
+ * configured */
+ int fw_encrypt_ok; /* whether firmware-based WEP encrypt is working
+ * in Host AP mode (STA f/w 1.4.9 or newer) */
+ int bcrx_sta_key; /* use individual keys to override default keys even
+ * with RX of broad/multicast frames */
+
+ struct prism2_frag_entry frag_cache[PRISM2_FRAG_CACHE_LEN];
+ unsigned int frag_next_idx;
+
+ int ieee_802_1x; /* is IEEE 802.1X used */
+
+ int antsel_tx, antsel_rx;
+ int rts_threshold; /* dot11RTSThreshold */
+ int fragm_threshold; /* dot11FragmentationThreshold */
+ int auth_algs; /* PRISM2_AUTH_ flags */
+
+ int enh_sec; /* cnfEnhSecurity options (broadcast SSID hide/ignore) */
+ int tallies32; /* 32-bit tallies in use */
+
+ struct prism2_helper_functions *func;
+
+ u8 *pda;
+ int fw_ap;
+#define PRISM2_FW_VER(major, minor, variant) \
+(((major) << 16) | ((minor) << 8) | variant)
+ u32 sta_fw_ver;
+
+ /* Tasklets for handling hardware IRQ related operations outside hw IRQ
+ * handler */
+ struct tasklet_struct bap_tasklet;
+
+ struct tasklet_struct info_tasklet;
+ struct sk_buff_head info_list; /* info frames as skb's for
+ * info_tasklet */
+
+ struct hostap_tx_callback_info *tx_callback; /* registered TX callbacks
+ */
+
+ struct tasklet_struct rx_tasklet;
+ struct sk_buff_head rx_list;
+
+ struct tasklet_struct sta_tx_exc_tasklet;
+ struct sk_buff_head sta_tx_exc_list;
+
+ int host_roaming;
+ unsigned long last_join_time; /* time of last JoinRequest */
+ struct hfa384x_hostscan_result *last_scan_results;
+ int last_scan_results_count;
+ enum { PRISM2_SCAN, PRISM2_HOSTSCAN } last_scan_type;
+ struct work_struct info_queue;
+ long pending_info; /* bit field of pending info_queue items */
+#define PRISM2_INFO_PENDING_LINKSTATUS 0
+#define PRISM2_INFO_PENDING_SCANRESULTS 1
+ int prev_link_status; /* previous received LinkStatus info */
+ int prev_linkstatus_connected;
+ u8 preferred_ap[6]; /* use this AP if possible */
+
+#ifdef PRISM2_CALLBACK
+ void *callback_data; /* Can be used in callbacks; e.g., allocate
+ * on enable event and free on disable event.
+ * Host AP driver code does not touch this. */
+#endif /* PRISM2_CALLBACK */
+
+ wait_queue_head_t hostscan_wq;
+
+ /* Passive scan in Host AP mode */
+ struct timer_list passive_scan_timer;
+ int passive_scan_interval; /* in seconds, 0 = disabled */
+ int passive_scan_channel;
+ enum { PASSIVE_SCAN_WAIT, PASSIVE_SCAN_LISTEN } passive_scan_state;
+
+ struct timer_list tick_timer;
+ unsigned long last_tick_timer;
+ unsigned int sw_tick_stuck;
+
+ /* commsQuality / dBmCommsQuality data from periodic polling; only
+ * valid for Managed and Ad-hoc modes */
+ unsigned long last_comms_qual_update;
+ int comms_qual; /* in some odd unit.. */
+ int avg_signal; /* in dB (note: negative) */
+ int avg_noise; /* in dB (note: negative) */
+ struct work_struct comms_qual_update;
+
+ /* RSSI to dBm adjustment (for RX descriptor fields) */
+ int rssi_to_dBm; /* substract from RSSI to get approximate dBm value */
+
+ /* BSS list / protected by local->lock */
+ struct list_head bss_list;
+ int num_bss_info;
+ int wpa; /* WPA support enabled */
+ int tkip_countermeasures;
+ int drop_unencrypted;
+ /* Generic IEEE 802.11 info element to be added to
+ * ProbeResp/Beacon/(Re)AssocReq */
+ u8 *generic_elem;
+ size_t generic_elem_len;
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+ /* Persistent volatile download data */
+ struct prism2_download_data *dl_pri;
+ struct prism2_download_data *dl_sec;
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+#ifdef PRISM2_IO_DEBUG
+#define PRISM2_IO_DEBUG_SIZE 10000
+ u32 io_debug[PRISM2_IO_DEBUG_SIZE];
+ int io_debug_head;
+ int io_debug_enabled;
+#endif /* PRISM2_IO_DEBUG */
+
+ /* Pointer to hardware model specific (cs,pci,plx) private data. */
+ void *hw_priv;
+};
+
+
+/* Per interface private Host AP data
+ * Allocated for each net device that Host AP uses (wlan#, wlan#ap, wlan#sta,
+ * WDS) and netdev_priv(dev) points to this structure. */
+struct hostap_interface {
+ struct list_head list; /* list entry in Host AP interface list */
+ struct net_device *dev; /* pointer to this device */
+ struct local_info *local; /* pointer to shared private data */
+ struct net_device_stats stats;
+ struct iw_spy_data spy_data; /* iwspy support */
+ struct iw_public_data wireless_data;
+
+ enum {
+ HOSTAP_INTERFACE_MASTER,
+ HOSTAP_INTERFACE_MAIN,
+ HOSTAP_INTERFACE_AP,
+ HOSTAP_INTERFACE_STA,
+ HOSTAP_INTERFACE_WDS,
+ } type;
+
+ union {
+ struct hostap_interface_wds {
+ u8 remote_addr[ETH_ALEN];
+ } wds;
+ } u;
+};
+
+
+#define HOSTAP_SKB_TX_DATA_MAGIC 0xf08a36a2
+
+/*
+ * TX meta data - stored in skb->cb buffer, so this must not be increased over
+ * the 40-byte limit
+ */
+struct hostap_skb_tx_data {
+ u32 magic; /* HOSTAP_SKB_TX_DATA_MAGIC */
+ u8 rate; /* transmit rate */
+#define HOSTAP_TX_FLAGS_WDS BIT(0)
+#define HOSTAP_TX_FLAGS_BUFFERED_FRAME BIT(1)
+#define HOSTAP_TX_FLAGS_ADD_MOREDATA BIT(2)
+ u8 flags; /* HOSTAP_TX_FLAGS_* */
+ u16 tx_cb_idx;
+ struct hostap_interface *iface;
+ unsigned long jiffies; /* queueing timestamp */
+ unsigned short ethertype;
+};
+
+
+#ifndef PRISM2_NO_DEBUG
+
+#define DEBUG_FID BIT(0)
+#define DEBUG_PS BIT(1)
+#define DEBUG_FLOW BIT(2)
+#define DEBUG_AP BIT(3)
+#define DEBUG_HW BIT(4)
+#define DEBUG_EXTRA BIT(5)
+#define DEBUG_EXTRA2 BIT(6)
+#define DEBUG_PS2 BIT(7)
+#define DEBUG_MASK (DEBUG_PS | DEBUG_AP | DEBUG_HW | DEBUG_EXTRA)
+#define PDEBUG(n, args...) \
+do { if ((n) & DEBUG_MASK) printk(KERN_DEBUG args); } while (0)
+#define PDEBUG2(n, args...) \
+do { if ((n) & DEBUG_MASK) printk(args); } while (0)
+
+#else /* PRISM2_NO_DEBUG */
+
+#define PDEBUG(n, args...)
+#define PDEBUG2(n, args...)
+
+#endif /* PRISM2_NO_DEBUG */
+
+enum { BAP0 = 0, BAP1 = 1 };
+
+#define PRISM2_IO_DEBUG_CMD_INB 0
+#define PRISM2_IO_DEBUG_CMD_INW 1
+#define PRISM2_IO_DEBUG_CMD_INSW 2
+#define PRISM2_IO_DEBUG_CMD_OUTB 3
+#define PRISM2_IO_DEBUG_CMD_OUTW 4
+#define PRISM2_IO_DEBUG_CMD_OUTSW 5
+#define PRISM2_IO_DEBUG_CMD_ERROR 6
+#define PRISM2_IO_DEBUG_CMD_INTERRUPT 7
+
+#ifdef PRISM2_IO_DEBUG
+
+#define PRISM2_IO_DEBUG_ENTRY(cmd, reg, value) \
+(((cmd) << 24) | ((reg) << 16) | value)
+
+static inline void prism2_io_debug_add(struct net_device *dev, int cmd,
+ int reg, int value)
+{
+ struct hostap_interface *iface = netdev_priv(dev);
+ local_info_t *local = iface->local;
+
+ if (!local->io_debug_enabled)
+ return;
+
+ local->io_debug[local->io_debug_head] = jiffies & 0xffffffff;
+ if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE)
+ local->io_debug_head = 0;
+ local->io_debug[local->io_debug_head] =
+ PRISM2_IO_DEBUG_ENTRY(cmd, reg, value);
+ if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE)
+ local->io_debug_head = 0;
+}
+
+
+static inline void prism2_io_debug_error(struct net_device *dev, int err)
+{
+ struct hostap_interface *iface = netdev_priv(dev);
+ local_info_t *local = iface->local;
+ unsigned long flags;
+
+ if (!local->io_debug_enabled)
+ return;
+
+ spin_lock_irqsave(&local->lock, flags);
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_ERROR, 0, err);
+ if (local->io_debug_enabled == 1) {
+ local->io_debug_enabled = 0;
+ printk(KERN_DEBUG "%s: I/O debug stopped\n", dev->name);
+ }
+ spin_unlock_irqrestore(&local->lock, flags);
+}
+
+#else /* PRISM2_IO_DEBUG */
+
+static inline void prism2_io_debug_add(struct net_device *dev, int cmd,
+ int reg, int value)
+{
+}
+
+static inline void prism2_io_debug_error(struct net_device *dev, int err)
+{
+}
+
+#endif /* PRISM2_IO_DEBUG */
+
+
+#ifdef PRISM2_CALLBACK
+enum {
+ /* Called when card is enabled */
+ PRISM2_CALLBACK_ENABLE,
+
+ /* Called when card is disabled */
+ PRISM2_CALLBACK_DISABLE,
+
+ /* Called when RX/TX starts/ends */
+ PRISM2_CALLBACK_RX_START, PRISM2_CALLBACK_RX_END,
+ PRISM2_CALLBACK_TX_START, PRISM2_CALLBACK_TX_END
+};
+void prism2_callback(local_info_t *local, int event);
+#else /* PRISM2_CALLBACK */
+#define prism2_callback(d, e) do { } while (0)
+#endif /* PRISM2_CALLBACK */
+
+#endif /* __KERNEL__ */
+
+#endif /* HOSTAP_WLAN_H */
diff --git a/drivers/net/wireless/ieee802_11.h b/drivers/net/wireless/ieee802_11.h
deleted file mode 100644
index 53dd5248f9f1..000000000000
--- a/drivers/net/wireless/ieee802_11.h
+++ /dev/null
@@ -1,78 +0,0 @@
-#ifndef _IEEE802_11_H
-#define _IEEE802_11_H
-
-#define IEEE802_11_DATA_LEN 2304
-/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
- 6.2.1.1.2.
-
- The figure in section 7.1.2 suggests a body size of up to 2312
- bytes is allowed, which is a bit confusing, I suspect this
- represents the 2304 bytes of real data, plus a possible 8 bytes of
- WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */
-
-
-#define IEEE802_11_HLEN 30
-#define IEEE802_11_FRAME_LEN (IEEE802_11_DATA_LEN + IEEE802_11_HLEN)
-
-struct ieee802_11_hdr {
- u16 frame_ctl;
- u16 duration_id;
- u8 addr1[ETH_ALEN];
- u8 addr2[ETH_ALEN];
- u8 addr3[ETH_ALEN];
- u16 seq_ctl;
- u8 addr4[ETH_ALEN];
-} __attribute__ ((packed));
-
-/* Frame control field constants */
-#define IEEE802_11_FCTL_VERS 0x0002
-#define IEEE802_11_FCTL_FTYPE 0x000c
-#define IEEE802_11_FCTL_STYPE 0x00f0
-#define IEEE802_11_FCTL_TODS 0x0100
-#define IEEE802_11_FCTL_FROMDS 0x0200
-#define IEEE802_11_FCTL_MOREFRAGS 0x0400
-#define IEEE802_11_FCTL_RETRY 0x0800
-#define IEEE802_11_FCTL_PM 0x1000
-#define IEEE802_11_FCTL_MOREDATA 0x2000
-#define IEEE802_11_FCTL_WEP 0x4000
-#define IEEE802_11_FCTL_ORDER 0x8000
-
-#define IEEE802_11_FTYPE_MGMT 0x0000
-#define IEEE802_11_FTYPE_CTL 0x0004
-#define IEEE802_11_FTYPE_DATA 0x0008
-
-/* management */
-#define IEEE802_11_STYPE_ASSOC_REQ 0x0000
-#define IEEE802_11_STYPE_ASSOC_RESP 0x0010
-#define IEEE802_11_STYPE_REASSOC_REQ 0x0020
-#define IEEE802_11_STYPE_REASSOC_RESP 0x0030
-#define IEEE802_11_STYPE_PROBE_REQ 0x0040
-#define IEEE802_11_STYPE_PROBE_RESP 0x0050
-#define IEEE802_11_STYPE_BEACON 0x0080
-#define IEEE802_11_STYPE_ATIM 0x0090
-#define IEEE802_11_STYPE_DISASSOC 0x00A0
-#define IEEE802_11_STYPE_AUTH 0x00B0
-#define IEEE802_11_STYPE_DEAUTH 0x00C0
-
-/* control */
-#define IEEE802_11_STYPE_PSPOLL 0x00A0
-#define IEEE802_11_STYPE_RTS 0x00B0
-#define IEEE802_11_STYPE_CTS 0x00C0
-#define IEEE802_11_STYPE_ACK 0x00D0
-#define IEEE802_11_STYPE_CFEND 0x00E0
-#define IEEE802_11_STYPE_CFENDACK 0x00F0
-
-/* data */
-#define IEEE802_11_STYPE_DATA 0x0000
-#define IEEE802_11_STYPE_DATA_CFACK 0x0010
-#define IEEE802_11_STYPE_DATA_CFPOLL 0x0020
-#define IEEE802_11_STYPE_DATA_CFACKPOLL 0x0030
-#define IEEE802_11_STYPE_NULLFUNC 0x0040
-#define IEEE802_11_STYPE_CFACK 0x0050
-#define IEEE802_11_STYPE_CFPOLL 0x0060
-#define IEEE802_11_STYPE_CFACKPOLL 0x0070
-
-#define IEEE802_11_SCTL_FRAG 0x000F
-#define IEEE802_11_SCTL_SEQ 0xFFF0
-
-#endif /* _IEEE802_11_H */
diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c
new file mode 100644
index 000000000000..a47fce4beadf
--- /dev/null
+++ b/drivers/net/wireless/ipw2100.c
@@ -0,0 +1,8679 @@
+/******************************************************************************
+
+ Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+ Portions of this file are based on the sample_* files provided by Wireless
+ Extensions 0.26 package and copyright (c) 1997-2003 Jean Tourrilhes
+ <jt@hpl.hp.com>
+
+ Portions of this file are based on the Host AP project,
+ Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ <jkmaline@cc.hut.fi>
+ Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+
+ Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and
+ ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c
+ available in the 2.4.25 kernel sources, and are copyright (c) Alan Cox
+
+******************************************************************************/
+/*
+
+ Initial driver on which this is based was developed by Janusz Gorycki,
+ Maciej Urbaniak, and Maciej Sosnowski.
+
+ Promiscuous mode support added by Jacek Wysoczynski and Maciej Urbaniak.
+
+Theory of Operation
+
+Tx - Commands and Data
+
+Firmware and host share a circular queue of Transmit Buffer Descriptors (TBDs)
+Each TBD contains a pointer to the physical (dma_addr_t) address of data being
+sent to the firmware as well as the length of the data.
+
+The host writes to the TBD queue at the WRITE index. The WRITE index points
+to the _next_ packet to be written and is advanced when after the TBD has been
+filled.
+
+The firmware pulls from the TBD queue at the READ index. The READ index points
+to the currently being read entry, and is advanced once the firmware is
+done with a packet.
+
+When data is sent to the firmware, the first TBD is used to indicate to the
+firmware if a Command or Data is being sent. If it is Command, all of the
+command information is contained within the physical address referred to by the
+TBD. If it is Data, the first TBD indicates the type of data packet, number
+of fragments, etc. The next TBD then referrs to the actual packet location.
+
+The Tx flow cycle is as follows:
+
+1) ipw2100_tx() is called by kernel with SKB to transmit
+2) Packet is move from the tx_free_list and appended to the transmit pending
+ list (tx_pend_list)
+3) work is scheduled to move pending packets into the shared circular queue.
+4) when placing packet in the circular queue, the incoming SKB is DMA mapped
+ to a physical address. That address is entered into a TBD. Two TBDs are
+ filled out. The first indicating a data packet, the second referring to the
+ actual payload data.
+5) the packet is removed from tx_pend_list and placed on the end of the
+ firmware pending list (fw_pend_list)
+6) firmware is notified that the WRITE index has
+7) Once the firmware has processed the TBD, INTA is triggered.
+8) For each Tx interrupt received from the firmware, the READ index is checked
+ to see which TBDs are done being processed.
+9) For each TBD that has been processed, the ISR pulls the oldest packet
+ from the fw_pend_list.
+10)The packet structure contained in the fw_pend_list is then used
+ to unmap the DMA address and to free the SKB originally passed to the driver
+ from the kernel.
+11)The packet structure is placed onto the tx_free_list
+
+The above steps are the same for commands, only the msg_free_list/msg_pend_list
+are used instead of tx_free_list/tx_pend_list
+
+...
+
+Critical Sections / Locking :
+
+There are two locks utilized. The first is the low level lock (priv->low_lock)
+that protects the following:
+
+- Access to the Tx/Rx queue lists via priv->low_lock. The lists are as follows:
+
+ tx_free_list : Holds pre-allocated Tx buffers.
+ TAIL modified in __ipw2100_tx_process()
+ HEAD modified in ipw2100_tx()
+
+ tx_pend_list : Holds used Tx buffers waiting to go into the TBD ring
+ TAIL modified ipw2100_tx()
+ HEAD modified by ipw2100_tx_send_data()
+
+ msg_free_list : Holds pre-allocated Msg (Command) buffers
+ TAIL modified in __ipw2100_tx_process()
+ HEAD modified in ipw2100_hw_send_command()
+
+ msg_pend_list : Holds used Msg buffers waiting to go into the TBD ring
+ TAIL modified in ipw2100_hw_send_command()
+ HEAD modified in ipw2100_tx_send_commands()
+
+ The flow of data on the TX side is as follows:
+
+ MSG_FREE_LIST + COMMAND => MSG_PEND_LIST => TBD => MSG_FREE_LIST
+ TX_FREE_LIST + DATA => TX_PEND_LIST => TBD => TX_FREE_LIST
+
+ The methods that work on the TBD ring are protected via priv->low_lock.
+
+- The internal data state of the device itself
+- Access to the firmware read/write indexes for the BD queues
+ and associated logic
+
+All external entry functions are locked with the priv->action_lock to ensure
+that only one external action is invoked at a time.
+
+
+*/
+
+#include <linux/compiler.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/unistd.h>
+#include <linux/stringify.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/time.h>
+#include <linux/firmware.h>
+#include <linux/acpi.h>
+#include <linux/ctype.h>
+
+#include "ipw2100.h"
+
+#define IPW2100_VERSION "1.1.0"
+
+#define DRV_NAME "ipw2100"
+#define DRV_VERSION IPW2100_VERSION
+#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver"
+#define DRV_COPYRIGHT "Copyright(c) 2003-2004 Intel Corporation"
+
+
+/* Debugging stuff */
+#ifdef CONFIG_IPW_DEBUG
+#define CONFIG_IPW2100_RX_DEBUG /* Reception debugging */
+#endif
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+static int mode = 0;
+static int channel = 0;
+static int associate = 1;
+static int disable = 0;
+#ifdef CONFIG_PM
+static struct ipw2100_fw ipw2100_firmware;
+#endif
+
+#include <linux/moduleparam.h>
+module_param(debug, int, 0444);
+module_param(mode, int, 0444);
+module_param(channel, int, 0444);
+module_param(associate, int, 0444);
+module_param(disable, int, 0444);
+
+MODULE_PARM_DESC(debug, "debug level");
+MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
+MODULE_PARM_DESC(channel, "channel");
+MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
+MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
+
+static u32 ipw2100_debug_level = IPW_DL_NONE;
+
+#ifdef CONFIG_IPW_DEBUG
+#define IPW_DEBUG(level, message...) \
+do { \
+ if (ipw2100_debug_level & (level)) { \
+ printk(KERN_DEBUG "ipw2100: %c %s ", \
+ in_interrupt() ? 'I' : 'U', __FUNCTION__); \
+ printk(message); \
+ } \
+} while (0)
+#else
+#define IPW_DEBUG(level, message...) do {} while (0)
+#endif /* CONFIG_IPW_DEBUG */
+
+#ifdef CONFIG_IPW_DEBUG
+static const char *command_types[] = {
+ "undefined",
+ "unused", /* HOST_ATTENTION */
+ "HOST_COMPLETE",
+ "unused", /* SLEEP */
+ "unused", /* HOST_POWER_DOWN */
+ "unused",
+ "SYSTEM_CONFIG",
+ "unused", /* SET_IMR */
+ "SSID",
+ "MANDATORY_BSSID",
+ "AUTHENTICATION_TYPE",
+ "ADAPTER_ADDRESS",
+ "PORT_TYPE",
+ "INTERNATIONAL_MODE",
+ "CHANNEL",
+ "RTS_THRESHOLD",
+ "FRAG_THRESHOLD",
+ "POWER_MODE",
+ "TX_RATES",
+ "BASIC_TX_RATES",
+ "WEP_KEY_INFO",
+ "unused",
+ "unused",
+ "unused",
+ "unused",
+ "WEP_KEY_INDEX",
+ "WEP_FLAGS",
+ "ADD_MULTICAST",
+ "CLEAR_ALL_MULTICAST",
+ "BEACON_INTERVAL",
+ "ATIM_WINDOW",
+ "CLEAR_STATISTICS",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "TX_POWER_INDEX",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "BROADCAST_SCAN",
+ "CARD_DISABLE",
+ "PREFERRED_BSSID",
+ "SET_SCAN_OPTIONS",
+ "SCAN_DWELL_TIME",
+ "SWEEP_TABLE",
+ "AP_OR_STATION_TABLE",
+ "GROUP_ORDINALS",
+ "SHORT_RETRY_LIMIT",
+ "LONG_RETRY_LIMIT",
+ "unused", /* SAVE_CALIBRATION */
+ "unused", /* RESTORE_CALIBRATION */
+ "undefined",
+ "undefined",
+ "undefined",
+ "HOST_PRE_POWER_DOWN",
+ "unused", /* HOST_INTERRUPT_COALESCING */
+ "undefined",
+ "CARD_DISABLE_PHY_OFF",
+ "MSDU_TX_RATES"
+ "undefined",
+ "undefined",
+ "SET_STATION_STAT_BITS",
+ "CLEAR_STATIONS_STAT_BITS",
+ "LEAP_ROGUE_MODE",
+ "SET_SECURITY_INFORMATION",
+ "DISASSOCIATION_BSSID",
+ "SET_WPA_ASS_IE"
+};
+#endif
+
+
+/* Pre-decl until we get the code solid and then we can clean it up */
+static void ipw2100_tx_send_commands(struct ipw2100_priv *priv);
+static void ipw2100_tx_send_data(struct ipw2100_priv *priv);
+static int ipw2100_adapter_setup(struct ipw2100_priv *priv);
+
+static void ipw2100_queues_initialize(struct ipw2100_priv *priv);
+static void ipw2100_queues_free(struct ipw2100_priv *priv);
+static int ipw2100_queues_allocate(struct ipw2100_priv *priv);
+
+static int ipw2100_fw_download(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static int ipw2100_get_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
+ size_t max);
+static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
+ size_t max);
+static void ipw2100_release_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static int ipw2100_ucode_download(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static void ipw2100_wx_event_work(struct ipw2100_priv *priv);
+static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device * dev);
+static struct iw_handler_def ipw2100_wx_handler_def;
+
+
+static inline void read_register(struct net_device *dev, u32 reg, u32 *val)
+{
+ *val = readl((void *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("r: 0x%08X => 0x%08X\n", reg, *val);
+}
+
+static inline void write_register(struct net_device *dev, u32 reg, u32 val)
+{
+ writel(val, (void *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("w: 0x%08X <= 0x%08X\n", reg, val);
+}
+
+static inline void read_register_word(struct net_device *dev, u32 reg, u16 *val)
+{
+ *val = readw((void *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("r: 0x%08X => %04X\n", reg, *val);
+}
+
+static inline void read_register_byte(struct net_device *dev, u32 reg, u8 *val)
+{
+ *val = readb((void *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("r: 0x%08X => %02X\n", reg, *val);
+}
+
+static inline void write_register_word(struct net_device *dev, u32 reg, u16 val)
+{
+ writew(val, (void *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("w: 0x%08X <= %04X\n", reg, val);
+}
+
+
+static inline void write_register_byte(struct net_device *dev, u32 reg, u8 val)
+{
+ writeb(val, (void *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("w: 0x%08X =< %02X\n", reg, val);
+}
+
+static inline void read_nic_dword(struct net_device *dev, u32 addr, u32 *val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ read_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_dword(struct net_device *dev, u32 addr, u32 val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void read_nic_word(struct net_device *dev, u32 addr, u16 *val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ read_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_word(struct net_device *dev, u32 addr, u16 val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ write_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void read_nic_byte(struct net_device *dev, u32 addr, u8 *val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_byte(struct net_device *dev, u32 addr, u8 val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_auto_inc_address(struct net_device *dev, u32 addr)
+{
+ write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+}
+
+static inline void write_nic_dword_auto_inc(struct net_device *dev, u32 val)
+{
+ write_register(dev, IPW_REG_AUTOINCREMENT_DATA, val);
+}
+
+static inline void write_nic_memory(struct net_device *dev, u32 addr, u32 len,
+ const u8 *buf)
+{
+ u32 aligned_addr;
+ u32 aligned_len;
+ u32 dif_len;
+ u32 i;
+
+ /* read first nibble byte by byte */
+ aligned_addr = addr & (~0x3);
+ dif_len = addr - aligned_addr;
+ if (dif_len) {
+ /* Start reading at aligned_addr + dif_len */
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ aligned_addr);
+ for (i = dif_len; i < 4; i++, buf++)
+ write_register_byte(
+ dev, IPW_REG_INDIRECT_ACCESS_DATA + i,
+ *buf);
+
+ len -= dif_len;
+ aligned_addr += 4;
+ }
+
+ /* read DWs through autoincrement registers */
+ write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS,
+ aligned_addr);
+ aligned_len = len & (~0x3);
+ for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+ write_register(
+ dev, IPW_REG_AUTOINCREMENT_DATA, *(u32 *)buf);
+
+ /* copy the last nibble */
+ dif_len = len - aligned_len;
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
+ for (i = 0; i < dif_len; i++, buf++)
+ write_register_byte(
+ dev, IPW_REG_INDIRECT_ACCESS_DATA + i, *buf);
+}
+
+static inline void read_nic_memory(struct net_device *dev, u32 addr, u32 len,
+ u8 *buf)
+{
+ u32 aligned_addr;
+ u32 aligned_len;
+ u32 dif_len;
+ u32 i;
+
+ /* read first nibble byte by byte */
+ aligned_addr = addr & (~0x3);
+ dif_len = addr - aligned_addr;
+ if (dif_len) {
+ /* Start reading at aligned_addr + dif_len */
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ aligned_addr);
+ for (i = dif_len; i < 4; i++, buf++)
+ read_register_byte(
+ dev, IPW_REG_INDIRECT_ACCESS_DATA + i, buf);
+
+ len -= dif_len;
+ aligned_addr += 4;
+ }
+
+ /* read DWs through autoincrement registers */
+ write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS,
+ aligned_addr);
+ aligned_len = len & (~0x3);
+ for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+ read_register(dev, IPW_REG_AUTOINCREMENT_DATA,
+ (u32 *)buf);
+
+ /* copy the last nibble */
+ dif_len = len - aligned_len;
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ aligned_addr);
+ for (i = 0; i < dif_len; i++, buf++)
+ read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA +
+ i, buf);
+}
+
+static inline int ipw2100_hw_is_adapter_in_system(struct net_device *dev)
+{
+ return (dev->base_addr &&
+ (readl((void *)(dev->base_addr + IPW_REG_DOA_DEBUG_AREA_START))
+ == IPW_DATA_DOA_DEBUG_VALUE));
+}
+
+static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord,
+ void *val, u32 *len)
+{
+ struct ipw2100_ordinals *ordinals = &priv->ordinals;
+ u32 addr;
+ u32 field_info;
+ u16 field_len;
+ u16 field_count;
+ u32 total_length;
+
+ if (ordinals->table1_addr == 0) {
+ printk(KERN_WARNING DRV_NAME ": attempt to use fw ordinals "
+ "before they have been loaded.\n");
+ return -EINVAL;
+ }
+
+ if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
+ if (*len < IPW_ORD_TAB_1_ENTRY_SIZE) {
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+ printk(KERN_WARNING DRV_NAME
+ ": ordinal buffer length too small, need %zd\n",
+ IPW_ORD_TAB_1_ENTRY_SIZE);
+
+ return -EINVAL;
+ }
+
+ read_nic_dword(priv->net_dev, ordinals->table1_addr + (ord << 2),
+ &addr);
+ read_nic_dword(priv->net_dev, addr, val);
+
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+ return 0;
+ }
+
+ if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) {
+
+ ord -= IPW_START_ORD_TAB_2;
+
+ /* get the address of statistic */
+ read_nic_dword(priv->net_dev, ordinals->table2_addr + (ord << 3),
+ &addr);
+
+ /* get the second DW of statistics ;
+ * two 16-bit words - first is length, second is count */
+ read_nic_dword(priv->net_dev,
+ ordinals->table2_addr + (ord << 3) + sizeof(u32),
+ &field_info);
+
+ /* get each entry length */
+ field_len = *((u16 *)&field_info);
+
+ /* get number of entries */
+ field_count = *(((u16 *)&field_info) + 1);
+
+ /* abort if no enought memory */
+ total_length = field_len * field_count;
+ if (total_length > *len) {
+ *len = total_length;
+ return -EINVAL;
+ }
+
+ *len = total_length;
+ if (!total_length)
+ return 0;
+
+ /* read the ordinal data from the SRAM */
+ read_nic_memory(priv->net_dev, addr, total_length, val);
+
+ return 0;
+ }
+
+ printk(KERN_WARNING DRV_NAME ": ordinal %d neither in table 1 nor "
+ "in table 2\n", ord);
+
+ return -EINVAL;
+}
+
+static int ipw2100_set_ordinal(struct ipw2100_priv *priv, u32 ord, u32 *val,
+ u32 *len)
+{
+ struct ipw2100_ordinals *ordinals = &priv->ordinals;
+ u32 addr;
+
+ if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
+ if (*len != IPW_ORD_TAB_1_ENTRY_SIZE) {
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+ IPW_DEBUG_INFO("wrong size\n");
+ return -EINVAL;
+ }
+
+ read_nic_dword(priv->net_dev, ordinals->table1_addr + (ord << 2),
+ &addr);
+
+ write_nic_dword(priv->net_dev, addr, *val);
+
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+ return 0;
+ }
+
+ IPW_DEBUG_INFO("wrong table\n");
+ if (IS_ORDINAL_TABLE_TWO(ordinals, ord))
+ return -EINVAL;
+
+ return -EINVAL;
+}
+
+static char *snprint_line(char *buf, size_t count,
+ const u8 *data, u32 len, u32 ofs)
+{
+ int out, i, j, l;
+ char c;
+
+ out = snprintf(buf, count, "%08X", ofs);
+
+ for (l = 0, i = 0; i < 2; i++) {
+ out += snprintf(buf + out, count - out, " ");
+ for (j = 0; j < 8 && l < len; j++, l++)
+ out += snprintf(buf + out, count - out, "%02X ",
+ data[(i * 8 + j)]);
+ for (; j < 8; j++)
+ out += snprintf(buf + out, count - out, " ");
+ }
+
+ out += snprintf(buf + out, count - out, " ");
+ for (l = 0, i = 0; i < 2; i++) {
+ out += snprintf(buf + out, count - out, " ");
+ for (j = 0; j < 8 && l < len; j++, l++) {
+ c = data[(i * 8 + j)];
+ if (!isascii(c) || !isprint(c))
+ c = '.';
+
+ out += snprintf(buf + out, count - out, "%c", c);
+ }
+
+ for (; j < 8; j++)
+ out += snprintf(buf + out, count - out, " ");
+ }
+
+ return buf;
+}
+
+static void printk_buf(int level, const u8 *data, u32 len)
+{
+ char line[81];
+ u32 ofs = 0;
+ if (!(ipw2100_debug_level & level))
+ return;
+
+ while (len) {
+ printk(KERN_DEBUG "%s\n",
+ snprint_line(line, sizeof(line), &data[ofs],
+ min(len, 16U), ofs));
+ ofs += 16;
+ len -= min(len, 16U);
+ }
+}
+
+
+
+#define MAX_RESET_BACKOFF 10
+
+static inline void schedule_reset(struct ipw2100_priv *priv)
+{
+ unsigned long now = get_seconds();
+
+ /* If we haven't received a reset request within the backoff period,
+ * then we can reset the backoff interval so this reset occurs
+ * immediately */
+ if (priv->reset_backoff &&
+ (now - priv->last_reset > priv->reset_backoff))
+ priv->reset_backoff = 0;
+
+ priv->last_reset = get_seconds();
+
+ if (!(priv->status & STATUS_RESET_PENDING)) {
+ IPW_DEBUG_INFO("%s: Scheduling firmware restart (%ds).\n",
+ priv->net_dev->name, priv->reset_backoff);
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+ priv->status |= STATUS_RESET_PENDING;
+ if (priv->reset_backoff)
+ queue_delayed_work(priv->workqueue, &priv->reset_work,
+ priv->reset_backoff * HZ);
+ else
+ queue_work(priv->workqueue, &priv->reset_work);
+
+ if (priv->reset_backoff < MAX_RESET_BACKOFF)
+ priv->reset_backoff++;
+
+ wake_up_interruptible(&priv->wait_command_queue);
+ } else
+ IPW_DEBUG_INFO("%s: Firmware restart already in progress.\n",
+ priv->net_dev->name);
+
+}
+
+#define HOST_COMPLETE_TIMEOUT (2 * HZ)
+static int ipw2100_hw_send_command(struct ipw2100_priv *priv,
+ struct host_command * cmd)
+{
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ unsigned long flags;
+ int err = 0;
+
+ IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n",
+ command_types[cmd->host_command], cmd->host_command,
+ cmd->host_command_length);
+ printk_buf(IPW_DL_HC, (u8*)cmd->host_command_parameters,
+ cmd->host_command_length);
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (priv->fatal_error) {
+ IPW_DEBUG_INFO("Attempt to send command while hardware in fatal error condition.\n");
+ err = -EIO;
+ goto fail_unlock;
+ }
+
+ if (!(priv->status & STATUS_RUNNING)) {
+ IPW_DEBUG_INFO("Attempt to send command while hardware is not running.\n");
+ err = -EIO;
+ goto fail_unlock;
+ }
+
+ if (priv->status & STATUS_CMD_ACTIVE) {
+ IPW_DEBUG_INFO("Attempt to send command while another command is pending.\n");
+ err = -EBUSY;
+ goto fail_unlock;
+ }
+
+ if (list_empty(&priv->msg_free_list)) {
+ IPW_DEBUG_INFO("no available msg buffers\n");
+ goto fail_unlock;
+ }
+
+ priv->status |= STATUS_CMD_ACTIVE;
+ priv->messages_sent++;
+
+ element = priv->msg_free_list.next;
+
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+ packet->jiffy_start = jiffies;
+
+ /* initialize the firmware command packet */
+ packet->info.c_struct.cmd->host_command_reg = cmd->host_command;
+ packet->info.c_struct.cmd->host_command_reg1 = cmd->host_command1;
+ packet->info.c_struct.cmd->host_command_len_reg = cmd->host_command_length;
+ packet->info.c_struct.cmd->sequence = cmd->host_command_sequence;
+
+ memcpy(packet->info.c_struct.cmd->host_command_params_reg,
+ cmd->host_command_parameters,
+ sizeof(packet->info.c_struct.cmd->host_command_params_reg));
+
+ list_del(element);
+ DEC_STAT(&priv->msg_free_stat);
+
+ list_add_tail(element, &priv->msg_pend_list);
+ INC_STAT(&priv->msg_pend_stat);
+
+ ipw2100_tx_send_commands(priv);
+ ipw2100_tx_send_data(priv);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ /*
+ * We must wait for this command to complete before another
+ * command can be sent... but if we wait more than 3 seconds
+ * then there is a problem.
+ */
+
+ err = wait_event_interruptible_timeout(
+ priv->wait_command_queue, !(priv->status & STATUS_CMD_ACTIVE),
+ HOST_COMPLETE_TIMEOUT);
+
+ if (err == 0) {
+ IPW_DEBUG_INFO("Command completion failed out after %dms.\n",
+ HOST_COMPLETE_TIMEOUT / (HZ / 100));
+ priv->fatal_error = IPW2100_ERR_MSG_TIMEOUT;
+ priv->status &= ~STATUS_CMD_ACTIVE;
+ schedule_reset(priv);
+ return -EIO;
+ }
+
+ if (priv->fatal_error) {
+ printk(KERN_WARNING DRV_NAME ": %s: firmware fatal error\n",
+ priv->net_dev->name);
+ return -EIO;
+ }
+
+ /* !!!!! HACK TEST !!!!!
+ * When lots of debug trace statements are enabled, the driver
+ * doesn't seem to have as many firmware restart cycles...
+ *
+ * As a test, we're sticking in a 1/100s delay here */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ / 100);
+
+ return 0;
+
+ fail_unlock:
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ return err;
+}
+
+
+/*
+ * Verify the values and data access of the hardware
+ * No locks needed or used. No functions called.
+ */
+static int ipw2100_verify(struct ipw2100_priv *priv)
+{
+ u32 data1, data2;
+ u32 address;
+
+ u32 val1 = 0x76543210;
+ u32 val2 = 0xFEDCBA98;
+
+ /* Domain 0 check - all values should be DOA_DEBUG */
+ for (address = IPW_REG_DOA_DEBUG_AREA_START;
+ address < IPW_REG_DOA_DEBUG_AREA_END;
+ address += sizeof(u32)) {
+ read_register(priv->net_dev, address, &data1);
+ if (data1 != IPW_DATA_DOA_DEBUG_VALUE)
+ return -EIO;
+ }
+
+ /* Domain 1 check - use arbitrary read/write compare */
+ for (address = 0; address < 5; address++) {
+ /* The memory area is not used now */
+ write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
+ val1);
+ write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
+ val2);
+ read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
+ &data1);
+ read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
+ &data2);
+ if (val1 == data1 && val2 == data2)
+ return 0;
+ }
+
+ return -EIO;
+}
+
+/*
+ *
+ * Loop until the CARD_DISABLED bit is the same value as the
+ * supplied parameter
+ *
+ * TODO: See if it would be more efficient to do a wait/wake
+ * cycle and have the completion event trigger the wakeup
+ *
+ */
+#define IPW_CARD_DISABLE_COMPLETE_WAIT 100 // 100 milli
+static int ipw2100_wait_for_card_state(struct ipw2100_priv *priv, int state)
+{
+ int i;
+ u32 card_state;
+ u32 len = sizeof(card_state);
+ int err;
+
+ for (i = 0; i <= IPW_CARD_DISABLE_COMPLETE_WAIT * 1000; i += 50) {
+ err = ipw2100_get_ordinal(priv, IPW_ORD_CARD_DISABLED,
+ &card_state, &len);
+ if (err) {
+ IPW_DEBUG_INFO("Query of CARD_DISABLED ordinal "
+ "failed.\n");
+ return 0;
+ }
+
+ /* We'll break out if either the HW state says it is
+ * in the state we want, or if HOST_COMPLETE command
+ * finishes */
+ if ((card_state == state) ||
+ ((priv->status & STATUS_ENABLED) ?
+ IPW_HW_STATE_ENABLED : IPW_HW_STATE_DISABLED) == state) {
+ if (state == IPW_HW_STATE_ENABLED)
+ priv->status |= STATUS_ENABLED;
+ else
+ priv->status &= ~STATUS_ENABLED;
+
+ return 0;
+ }
+
+ udelay(50);
+ }
+
+ IPW_DEBUG_INFO("ipw2100_wait_for_card_state to %s state timed out\n",
+ state ? "DISABLED" : "ENABLED");
+ return -EIO;
+}
+
+
+/*********************************************************************
+ Procedure : sw_reset_and_clock
+ Purpose : Asserts s/w reset, asserts clock initialization
+ and waits for clock stabilization
+ ********************************************************************/
+static int sw_reset_and_clock(struct ipw2100_priv *priv)
+{
+ int i;
+ u32 r;
+
+ // assert s/w reset
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+ // wait for clock stabilization
+ for (i = 0; i < 1000; i++) {
+ udelay(IPW_WAIT_RESET_ARC_COMPLETE_DELAY);
+
+ // check clock ready bit
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &r);
+ if (r & IPW_AUX_HOST_RESET_REG_PRINCETON_RESET)
+ break;
+ }
+
+ if (i == 1000)
+ return -EIO; // TODO: better error value
+
+ /* set "initialization complete" bit to move adapter to
+ * D0 state */
+ write_register(priv->net_dev, IPW_REG_GP_CNTRL,
+ IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE);
+
+ /* wait for clock stabilization */
+ for (i = 0; i < 10000; i++) {
+ udelay(IPW_WAIT_CLOCK_STABILIZATION_DELAY * 4);
+
+ /* check clock ready bit */
+ read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
+ if (r & IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY)
+ break;
+ }
+
+ if (i == 10000)
+ return -EIO; /* TODO: better error value */
+
+ /* set D0 standby bit */
+ read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
+ write_register(priv->net_dev, IPW_REG_GP_CNTRL,
+ r | IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
+
+ return 0;
+}
+
+/*********************************************************************
+ Procedure : ipw2100_download_firmware
+ Purpose : Initiaze adapter after power on.
+ The sequence is:
+ 1. assert s/w reset first!
+ 2. awake clocks & wait for clock stabilization
+ 3. hold ARC (don't ask me why...)
+ 4. load Dino ucode and reset/clock init again
+ 5. zero-out shared mem
+ 6. download f/w
+ *******************************************************************/
+static int ipw2100_download_firmware(struct ipw2100_priv *priv)
+{
+ u32 address;
+ int err;
+
+#ifndef CONFIG_PM
+ /* Fetch the firmware and microcode */
+ struct ipw2100_fw ipw2100_firmware;
+#endif
+
+ if (priv->fatal_error) {
+ IPW_DEBUG_ERROR("%s: ipw2100_download_firmware called after "
+ "fatal error %d. Interface must be brought down.\n",
+ priv->net_dev->name, priv->fatal_error);
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_PM
+ if (!ipw2100_firmware.version) {
+ err = ipw2100_get_firmware(priv, &ipw2100_firmware);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
+ priv->net_dev->name, err);
+ priv->fatal_error = IPW2100_ERR_FW_LOAD;
+ goto fail;
+ }
+ }
+#else
+ err = ipw2100_get_firmware(priv, &ipw2100_firmware);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
+ priv->net_dev->name, err);
+ priv->fatal_error = IPW2100_ERR_FW_LOAD;
+ goto fail;
+ }
+#endif
+ priv->firmware_version = ipw2100_firmware.version;
+
+ /* s/w reset and clock stabilization */
+ err = sw_reset_and_clock(priv);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: sw_reset_and_clock failed: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ err = ipw2100_verify(priv);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: ipw2100_verify failed: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ /* Hold ARC */
+ write_nic_dword(priv->net_dev,
+ IPW_INTERNAL_REGISTER_HALT_AND_RESET,
+ 0x80000000);
+
+ /* allow ARC to run */
+ write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
+
+ /* load microcode */
+ err = ipw2100_ucode_download(priv, &ipw2100_firmware);
+ if (err) {
+ printk(KERN_ERR DRV_NAME ": %s: Error loading microcode: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ /* release ARC */
+ write_nic_dword(priv->net_dev,
+ IPW_INTERNAL_REGISTER_HALT_AND_RESET,
+ 0x00000000);
+
+ /* s/w reset and clock stabilization (again!!!) */
+ err = sw_reset_and_clock(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME ": %s: sw_reset_and_clock failed: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ /* load f/w */
+ err = ipw2100_fw_download(priv, &ipw2100_firmware);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: Error loading firmware: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+#ifndef CONFIG_PM
+ /*
+ * When the .resume method of the driver is called, the other
+ * part of the system, i.e. the ide driver could still stay in
+ * the suspend stage. This prevents us from loading the firmware
+ * from the disk. --YZ
+ */
+
+ /* free any storage allocated for firmware image */
+ ipw2100_release_firmware(priv, &ipw2100_firmware);
+#endif
+
+ /* zero out Domain 1 area indirectly (Si requirement) */
+ for (address = IPW_HOST_FW_SHARED_AREA0;
+ address < IPW_HOST_FW_SHARED_AREA0_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_SHARED_AREA1;
+ address < IPW_HOST_FW_SHARED_AREA1_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_SHARED_AREA2;
+ address < IPW_HOST_FW_SHARED_AREA2_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_SHARED_AREA3;
+ address < IPW_HOST_FW_SHARED_AREA3_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_INTERRUPT_AREA;
+ address < IPW_HOST_FW_INTERRUPT_AREA_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+
+ return 0;
+
+ fail:
+ ipw2100_release_firmware(priv, &ipw2100_firmware);
+ return err;
+}
+
+static inline void ipw2100_enable_interrupts(struct ipw2100_priv *priv)
+{
+ if (priv->status & STATUS_INT_ENABLED)
+ return;
+ priv->status |= STATUS_INT_ENABLED;
+ write_register(priv->net_dev, IPW_REG_INTA_MASK, IPW_INTERRUPT_MASK);
+}
+
+static inline void ipw2100_disable_interrupts(struct ipw2100_priv *priv)
+{
+ if (!(priv->status & STATUS_INT_ENABLED))
+ return;
+ priv->status &= ~STATUS_INT_ENABLED;
+ write_register(priv->net_dev, IPW_REG_INTA_MASK, 0x0);
+}
+
+
+static void ipw2100_initialize_ordinals(struct ipw2100_priv *priv)
+{
+ struct ipw2100_ordinals *ord = &priv->ordinals;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1,
+ &ord->table1_addr);
+
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2,
+ &ord->table2_addr);
+
+ read_nic_dword(priv->net_dev, ord->table1_addr, &ord->table1_size);
+ read_nic_dword(priv->net_dev, ord->table2_addr, &ord->table2_size);
+
+ ord->table2_size &= 0x0000FFFF;
+
+ IPW_DEBUG_INFO("table 1 size: %d\n", ord->table1_size);
+ IPW_DEBUG_INFO("table 2 size: %d\n", ord->table2_size);
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static inline void ipw2100_hw_set_gpio(struct ipw2100_priv *priv)
+{
+ u32 reg = 0;
+ /*
+ * Set GPIO 3 writable by FW; GPIO 1 writable
+ * by driver and enable clock
+ */
+ reg = (IPW_BIT_GPIO_GPIO3_MASK | IPW_BIT_GPIO_GPIO1_ENABLE |
+ IPW_BIT_GPIO_LED_OFF);
+ write_register(priv->net_dev, IPW_REG_GPIO, reg);
+}
+
+static inline int rf_kill_active(struct ipw2100_priv *priv)
+{
+#define MAX_RF_KILL_CHECKS 5
+#define RF_KILL_CHECK_DELAY 40
+
+ unsigned short value = 0;
+ u32 reg = 0;
+ int i;
+
+ if (!(priv->hw_features & HW_FEATURE_RFKILL)) {
+ priv->status &= ~STATUS_RF_KILL_HW;
+ return 0;
+ }
+
+ for (i = 0; i < MAX_RF_KILL_CHECKS; i++) {
+ udelay(RF_KILL_CHECK_DELAY);
+ read_register(priv->net_dev, IPW_REG_GPIO, &reg);
+ value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1);
+ }
+
+ if (value == 0)
+ priv->status |= STATUS_RF_KILL_HW;
+ else
+ priv->status &= ~STATUS_RF_KILL_HW;
+
+ return (value == 0);
+}
+
+static int ipw2100_get_hw_features(struct ipw2100_priv *priv)
+{
+ u32 addr, len;
+ u32 val;
+
+ /*
+ * EEPROM_SRAM_DB_START_ADDRESS using ordinal in ordinal table 1
+ */
+ len = sizeof(addr);
+ if (ipw2100_get_ordinal(
+ priv, IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS,
+ &addr, &len)) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return -EIO;
+ }
+
+ IPW_DEBUG_INFO("EEPROM address: %08X\n", addr);
+
+ /*
+ * EEPROM version is the byte at offset 0xfd in firmware
+ * We read 4 bytes, then shift out the byte we actually want */
+ read_nic_dword(priv->net_dev, addr + 0xFC, &val);
+ priv->eeprom_version = (val >> 24) & 0xFF;
+ IPW_DEBUG_INFO("EEPROM version: %d\n", priv->eeprom_version);
+
+ /*
+ * HW RF Kill enable is bit 0 in byte at offset 0x21 in firmware
+ *
+ * notice that the EEPROM bit is reverse polarity, i.e.
+ * bit = 0 signifies HW RF kill switch is supported
+ * bit = 1 signifies HW RF kill switch is NOT supported
+ */
+ read_nic_dword(priv->net_dev, addr + 0x20, &val);
+ if (!((val >> 24) & 0x01))
+ priv->hw_features |= HW_FEATURE_RFKILL;
+
+ IPW_DEBUG_INFO("HW RF Kill: %ssupported.\n",
+ (priv->hw_features & HW_FEATURE_RFKILL) ?
+ "" : "not ");
+
+ return 0;
+}
+
+/*
+ * Start firmware execution after power on and intialization
+ * The sequence is:
+ * 1. Release ARC
+ * 2. Wait for f/w initialization completes;
+ */
+static int ipw2100_start_adapter(struct ipw2100_priv *priv)
+{
+ int i;
+ u32 inta, inta_mask, gpio;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->status & STATUS_RUNNING)
+ return 0;
+
+ /*
+ * Initialize the hw - drive adapter to DO state by setting
+ * init_done bit. Wait for clk_ready bit and Download
+ * fw & dino ucode
+ */
+ if (ipw2100_download_firmware(priv)) {
+ printk(KERN_ERR DRV_NAME ": %s: Failed to power on the adapter.\n",
+ priv->net_dev->name);
+ return -EIO;
+ }
+
+ /* Clear the Tx, Rx and Msg queues and the r/w indexes
+ * in the firmware RBD and TBD ring queue */
+ ipw2100_queues_initialize(priv);
+
+ ipw2100_hw_set_gpio(priv);
+
+ /* TODO -- Look at disabling interrupts here to make sure none
+ * get fired during FW initialization */
+
+ /* Release ARC - clear reset bit */
+ write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
+
+ /* wait for f/w intialization complete */
+ IPW_DEBUG_FW("Waiting for f/w initialization to complete...\n");
+ i = 5000;
+ do {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(40 * HZ / 1000);
+ /* Todo... wait for sync command ... */
+
+ read_register(priv->net_dev, IPW_REG_INTA, &inta);
+
+ /* check "init done" bit */
+ if (inta & IPW2100_INTA_FW_INIT_DONE) {
+ /* reset "init done" bit */
+ write_register(priv->net_dev, IPW_REG_INTA,
+ IPW2100_INTA_FW_INIT_DONE);
+ break;
+ }
+
+ /* check error conditions : we check these after the firmware
+ * check so that if there is an error, the interrupt handler
+ * will see it and the adapter will be reset */
+ if (inta &
+ (IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR)) {
+ /* clear error conditions */
+ write_register(priv->net_dev, IPW_REG_INTA,
+ IPW2100_INTA_FATAL_ERROR |
+ IPW2100_INTA_PARITY_ERROR);
+ }
+ } while (i--);
+
+ /* Clear out any pending INTAs since we aren't supposed to have
+ * interrupts enabled at this point... */
+ read_register(priv->net_dev, IPW_REG_INTA, &inta);
+ read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
+ inta &= IPW_INTERRUPT_MASK;
+ /* Clear out any pending interrupts */
+ if (inta & inta_mask)
+ write_register(priv->net_dev, IPW_REG_INTA, inta);
+
+ IPW_DEBUG_FW("f/w initialization complete: %s\n",
+ i ? "SUCCESS" : "FAILED");
+
+ if (!i) {
+ printk(KERN_WARNING DRV_NAME ": %s: Firmware did not initialize.\n",
+ priv->net_dev->name);
+ return -EIO;
+ }
+
+ /* allow firmware to write to GPIO1 & GPIO3 */
+ read_register(priv->net_dev, IPW_REG_GPIO, &gpio);
+
+ gpio |= (IPW_BIT_GPIO_GPIO1_MASK | IPW_BIT_GPIO_GPIO3_MASK);
+
+ write_register(priv->net_dev, IPW_REG_GPIO, gpio);
+
+ /* Ready to receive commands */
+ priv->status |= STATUS_RUNNING;
+
+ /* The adapter has been reset; we are not associated */
+ priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+static inline void ipw2100_reset_fatalerror(struct ipw2100_priv *priv)
+{
+ if (!priv->fatal_error)
+ return;
+
+ priv->fatal_errors[priv->fatal_index++] = priv->fatal_error;
+ priv->fatal_index %= IPW2100_ERROR_QUEUE;
+ priv->fatal_error = 0;
+}
+
+
+/* NOTE: Our interrupt is disabled when this method is called */
+static int ipw2100_power_cycle_adapter(struct ipw2100_priv *priv)
+{
+ u32 reg;
+ int i;
+
+ IPW_DEBUG_INFO("Power cycling the hardware.\n");
+
+ ipw2100_hw_set_gpio(priv);
+
+ /* Step 1. Stop Master Assert */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+
+ /* Step 2. Wait for stop Master Assert
+ * (not more then 50us, otherwise ret error */
+ i = 5;
+ do {
+ udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+ if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+ break;
+ } while(i--);
+
+ priv->status &= ~STATUS_RESET_PENDING;
+
+ if (!i) {
+ IPW_DEBUG_INFO("exit - waited too long for master assert stop\n");
+ return -EIO;
+ }
+
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+
+ /* Reset any fatal_error conditions */
+ ipw2100_reset_fatalerror(priv);
+
+ /* At this point, the adapter is now stopped and disabled */
+ priv->status &= ~(STATUS_RUNNING | STATUS_ASSOCIATING |
+ STATUS_ASSOCIATED | STATUS_ENABLED);
+
+ return 0;
+}
+
+/*
+ * Send the CARD_DISABLE_PHY_OFF comamnd to the card to disable it
+ *
+ * After disabling, if the card was associated, a STATUS_ASSN_LOST will be sent.
+ *
+ * STATUS_CARD_DISABLE_NOTIFICATION will be sent regardless of
+ * if STATUS_ASSN_LOST is sent.
+ */
+static int ipw2100_hw_phy_off(struct ipw2100_priv *priv)
+{
+
+#define HW_PHY_OFF_LOOP_DELAY (HZ / 5000)
+
+ struct host_command cmd = {
+ .host_command = CARD_DISABLE_PHY_OFF,
+ .host_command_sequence = 0,
+ .host_command_length = 0,
+ };
+ int err, i;
+ u32 val1, val2;
+
+ IPW_DEBUG_HC("CARD_DISABLE_PHY_OFF\n");
+
+ /* Turn off the radio */
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ for (i = 0; i < 2500; i++) {
+ read_nic_dword(priv->net_dev, IPW2100_CONTROL_REG, &val1);
+ read_nic_dword(priv->net_dev, IPW2100_COMMAND, &val2);
+
+ if ((val1 & IPW2100_CONTROL_PHY_OFF) &&
+ (val2 & IPW2100_COMMAND_PHY_OFF))
+ return 0;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HW_PHY_OFF_LOOP_DELAY);
+ }
+
+ return -EIO;
+}
+
+
+static int ipw2100_enable_adapter(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = HOST_COMPLETE,
+ .host_command_sequence = 0,
+ .host_command_length = 0
+ };
+ int err = 0;
+
+ IPW_DEBUG_HC("HOST_COMPLETE\n");
+
+ if (priv->status & STATUS_ENABLED)
+ return 0;
+
+ down(&priv->adapter_sem);
+
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_HC("Command aborted due to RF kill active.\n");
+ goto fail_up;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err) {
+ IPW_DEBUG_INFO("Failed to send HOST_COMPLETE command\n");
+ goto fail_up;
+ }
+
+ err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_ENABLED);
+ if (err) {
+ IPW_DEBUG_INFO(
+ "%s: card not responding to init command.\n",
+ priv->net_dev->name);
+ goto fail_up;
+ }
+
+ if (priv->stop_hang_check) {
+ priv->stop_hang_check = 0;
+ queue_delayed_work(priv->workqueue, &priv->hang_check, HZ / 2);
+ }
+
+fail_up:
+ up(&priv->adapter_sem);
+ return err;
+}
+
+static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv)
+{
+#define HW_POWER_DOWN_DELAY (HZ / 10)
+
+ struct host_command cmd = {
+ .host_command = HOST_PRE_POWER_DOWN,
+ .host_command_sequence = 0,
+ .host_command_length = 0,
+ };
+ int err, i;
+ u32 reg;
+
+ if (!(priv->status & STATUS_RUNNING))
+ return 0;
+
+ priv->status |= STATUS_STOPPING;
+
+ /* We can only shut down the card if the firmware is operational. So,
+ * if we haven't reset since a fatal_error, then we can not send the
+ * shutdown commands. */
+ if (!priv->fatal_error) {
+ /* First, make sure the adapter is enabled so that the PHY_OFF
+ * command can shut it down */
+ ipw2100_enable_adapter(priv);
+
+ err = ipw2100_hw_phy_off(priv);
+ if (err)
+ printk(KERN_WARNING DRV_NAME ": Error disabling radio %d\n", err);
+
+ /*
+ * If in D0-standby mode going directly to D3 may cause a
+ * PCI bus violation. Therefore we must change out of the D0
+ * state.
+ *
+ * Sending the PREPARE_FOR_POWER_DOWN will restrict the
+ * hardware from going into standby mode and will transition
+ * out of D0-standy if it is already in that state.
+ *
+ * STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the
+ * driver upon completion. Once received, the driver can
+ * proceed to the D3 state.
+ *
+ * Prepare for power down command to fw. This command would
+ * take HW out of D0-standby and prepare it for D3 state.
+ *
+ * Currently FW does not support event notification for this
+ * event. Therefore, skip waiting for it. Just wait a fixed
+ * 100ms
+ */
+ IPW_DEBUG_HC("HOST_PRE_POWER_DOWN\n");
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ printk(KERN_WARNING DRV_NAME ": "
+ "%s: Power down command failed: Error %d\n",
+ priv->net_dev->name, err);
+ else {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HW_POWER_DOWN_DELAY);
+ }
+ }
+
+ priv->status &= ~STATUS_ENABLED;
+
+ /*
+ * Set GPIO 3 writable by FW; GPIO 1 writable
+ * by driver and enable clock
+ */
+ ipw2100_hw_set_gpio(priv);
+
+ /*
+ * Power down adapter. Sequence:
+ * 1. Stop master assert (RESET_REG[9]=1)
+ * 2. Wait for stop master (RESET_REG[8]==1)
+ * 3. S/w reset assert (RESET_REG[7] = 1)
+ */
+
+ /* Stop master assert */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+
+ /* wait stop master not more than 50 usec.
+ * Otherwise return error. */
+ for (i = 5; i > 0; i--) {
+ udelay(10);
+
+ /* Check master stop bit */
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+ if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+ break;
+ }
+
+ if (i == 0)
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Could now power down adapter.\n",
+ priv->net_dev->name);
+
+ /* assert s/w reset */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+ priv->status &= ~(STATUS_RUNNING | STATUS_STOPPING);
+
+ return 0;
+}
+
+
+static int ipw2100_disable_adapter(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = CARD_DISABLE,
+ .host_command_sequence = 0,
+ .host_command_length = 0
+ };
+ int err = 0;
+
+ IPW_DEBUG_HC("CARD_DISABLE\n");
+
+ if (!(priv->status & STATUS_ENABLED))
+ return 0;
+
+ /* Make sure we clear the associated state */
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+
+ if (!priv->stop_hang_check) {
+ priv->stop_hang_check = 1;
+ cancel_delayed_work(&priv->hang_check);
+ }
+
+ down(&priv->adapter_sem);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME ": exit - failed to send CARD_DISABLE command\n");
+ goto fail_up;
+ }
+
+ err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_DISABLED);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME ": exit - card failed to change to DISABLED\n");
+ goto fail_up;
+ }
+
+ IPW_DEBUG_INFO("TODO: implement scan state machine\n");
+
+fail_up:
+ up(&priv->adapter_sem);
+ return err;
+}
+
+static int ipw2100_set_scan_options(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = SET_SCAN_OPTIONS,
+ .host_command_sequence = 0,
+ .host_command_length = 8
+ };
+ int err;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ IPW_DEBUG_SCAN("setting scan options\n");
+
+ cmd.host_command_parameters[0] = 0;
+
+ if (!(priv->config & CFG_ASSOCIATE))
+ cmd.host_command_parameters[0] |= IPW_SCAN_NOASSOCIATE;
+ if ((priv->sec.flags & SEC_ENABLED) && priv->sec.enabled)
+ cmd.host_command_parameters[0] |= IPW_SCAN_MIXED_CELL;
+ if (priv->config & CFG_PASSIVE_SCAN)
+ cmd.host_command_parameters[0] |= IPW_SCAN_PASSIVE;
+
+ cmd.host_command_parameters[1] = priv->channel_mask;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ IPW_DEBUG_HC("SET_SCAN_OPTIONS 0x%04X\n",
+ cmd.host_command_parameters[0]);
+
+ return err;
+}
+
+static int ipw2100_start_scan(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = BROADCAST_SCAN,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ IPW_DEBUG_HC("START_SCAN\n");
+
+ cmd.host_command_parameters[0] = 0;
+
+ /* No scanning if in monitor mode */
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+ return 1;
+
+ if (priv->status & STATUS_SCANNING) {
+ IPW_DEBUG_SCAN("Scan requested while already in scan...\n");
+ return 0;
+ }
+
+ IPW_DEBUG_INFO("enter\n");
+
+ /* Not clearing here; doing so makes iwlist always return nothing...
+ *
+ * We should modify the table logic to use aging tables vs. clearing
+ * the table on each scan start.
+ */
+ IPW_DEBUG_SCAN("starting scan\n");
+
+ priv->status |= STATUS_SCANNING;
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ priv->status &= ~STATUS_SCANNING;
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return err;
+}
+
+static int ipw2100_up(struct ipw2100_priv *priv, int deferred)
+{
+ unsigned long flags;
+ int rc = 0;
+ u32 lock;
+ u32 ord_len = sizeof(lock);
+
+ /* Quite if manually disabled. */
+ if (priv->status & STATUS_RF_KILL_SW) {
+ IPW_DEBUG_INFO("%s: Radio is disabled by Manual Disable "
+ "switch\n", priv->net_dev->name);
+ return 0;
+ }
+
+ /* If the interrupt is enabled, turn it off... */
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_disable_interrupts(priv);
+
+ /* Reset any fatal_error conditions */
+ ipw2100_reset_fatalerror(priv);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ if (priv->status & STATUS_POWERED ||
+ (priv->status & STATUS_RESET_PENDING)) {
+ /* Power cycle the card ... */
+ if (ipw2100_power_cycle_adapter(priv)) {
+ printk(KERN_WARNING DRV_NAME ": %s: Could not cycle adapter.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+ } else
+ priv->status |= STATUS_POWERED;
+
+ /* Load the firmware, start the clocks, etc. */
+ if (ipw2100_start_adapter(priv)) {
+ printk(KERN_ERR DRV_NAME ": %s: Failed to start the firmware.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ ipw2100_initialize_ordinals(priv);
+
+ /* Determine capabilities of this particular HW configuration */
+ if (ipw2100_get_hw_features(priv)) {
+ printk(KERN_ERR DRV_NAME ": %s: Failed to determine HW features.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ lock = LOCK_NONE;
+ if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) {
+ printk(KERN_ERR DRV_NAME ": %s: Failed to clear ordinal lock.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ priv->status &= ~STATUS_SCANNING;
+
+ if (rf_kill_active(priv)) {
+ printk(KERN_INFO "%s: Radio is disabled by RF switch.\n",
+ priv->net_dev->name);
+
+ if (priv->stop_rf_kill) {
+ priv->stop_rf_kill = 0;
+ queue_delayed_work(priv->workqueue, &priv->rf_kill, HZ);
+ }
+
+ deferred = 1;
+ }
+
+ /* Turn on the interrupt so that commands can be processed */
+ ipw2100_enable_interrupts(priv);
+
+ /* Send all of the commands that must be sent prior to
+ * HOST_COMPLETE */
+ if (ipw2100_adapter_setup(priv)) {
+ printk(KERN_ERR DRV_NAME ": %s: Failed to start the card.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ if (!deferred) {
+ /* Enable the adapter - sends HOST_COMPLETE */
+ if (ipw2100_enable_adapter(priv)) {
+ printk(KERN_ERR DRV_NAME ": "
+ "%s: failed in call to enable adapter.\n",
+ priv->net_dev->name);
+ ipw2100_hw_stop_adapter(priv);
+ rc = 1;
+ goto exit;
+ }
+
+
+ /* Start a scan . . . */
+ ipw2100_set_scan_options(priv);
+ ipw2100_start_scan(priv);
+ }
+
+ exit:
+ return rc;
+}
+
+/* Called by register_netdev() */
+static int ipw2100_net_init(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return ipw2100_up(priv, 1);
+}
+
+static void ipw2100_down(struct ipw2100_priv *priv)
+{
+ unsigned long flags;
+ union iwreq_data wrqu = {
+ .ap_addr = {
+ .sa_family = ARPHRD_ETHER
+ }
+ };
+ int associated = priv->status & STATUS_ASSOCIATED;
+
+ /* Kill the RF switch timer */
+ if (!priv->stop_rf_kill) {
+ priv->stop_rf_kill = 1;
+ cancel_delayed_work(&priv->rf_kill);
+ }
+
+ /* Kill the firmare hang check timer */
+ if (!priv->stop_hang_check) {
+ priv->stop_hang_check = 1;
+ cancel_delayed_work(&priv->hang_check);
+ }
+
+ /* Kill any pending resets */
+ if (priv->status & STATUS_RESET_PENDING)
+ cancel_delayed_work(&priv->reset_work);
+
+ /* Make sure the interrupt is on so that FW commands will be
+ * processed correctly */
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_enable_interrupts(priv);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ if (ipw2100_hw_stop_adapter(priv))
+ printk(KERN_ERR DRV_NAME ": %s: Error stopping adapter.\n",
+ priv->net_dev->name);
+
+ /* Do not disable the interrupt until _after_ we disable
+ * the adaptor. Otherwise the CARD_DISABLE command will never
+ * be ack'd by the firmware */
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_disable_interrupts(priv);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+#ifdef ACPI_CSTATE_LIMIT_DEFINED
+ if (priv->config & CFG_C3_DISABLED) {
+ IPW_DEBUG_INFO(DRV_NAME ": Resetting C3 transitions.\n");
+ acpi_set_cstate_limit(priv->cstate_limit);
+ priv->config &= ~CFG_C3_DISABLED;
+ }
+#endif
+
+ /* We have to signal any supplicant if we are disassociating */
+ if (associated)
+ wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+}
+
+static void ipw2100_reset_adapter(struct ipw2100_priv *priv)
+{
+ unsigned long flags;
+ union iwreq_data wrqu = {
+ .ap_addr = {
+ .sa_family = ARPHRD_ETHER
+ }
+ };
+ int associated = priv->status & STATUS_ASSOCIATED;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+ IPW_DEBUG_INFO(DRV_NAME ": %s: Restarting adapter.\n",
+ priv->net_dev->name);
+ priv->resets++;
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+ priv->status |= STATUS_SECURITY_UPDATED;
+
+ /* Force a power cycle even if interface hasn't been opened
+ * yet */
+ cancel_delayed_work(&priv->reset_work);
+ priv->status |= STATUS_RESET_PENDING;
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ down(&priv->action_sem);
+ /* stop timed checks so that they don't interfere with reset */
+ priv->stop_hang_check = 1;
+ cancel_delayed_work(&priv->hang_check);
+
+ /* We have to signal any supplicant if we are disassociating */
+ if (associated)
+ wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+
+ ipw2100_up(priv, 0);
+ up(&priv->action_sem);
+
+}
+
+
+static void isr_indicate_associated(struct ipw2100_priv *priv, u32 status)
+{
+
+#define MAC_ASSOCIATION_READ_DELAY (HZ)
+ int ret, len, essid_len;
+ char essid[IW_ESSID_MAX_SIZE];
+ u32 txrate;
+ u32 chan;
+ char *txratename;
+ u8 bssid[ETH_ALEN];
+
+ /*
+ * TBD: BSSID is usually 00:00:00:00:00:00 here and not
+ * an actual MAC of the AP. Seems like FW sets this
+ * address too late. Read it later and expose through
+ * /proc or schedule a later task to query and update
+ */
+
+ essid_len = IW_ESSID_MAX_SIZE;
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID,
+ essid, &essid_len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+
+ len = sizeof(u32);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE,
+ &txrate, &len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+
+ len = sizeof(u32);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+ len = ETH_ALEN;
+ ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, &bssid, &len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+ memcpy(priv->ieee->bssid, bssid, ETH_ALEN);
+
+
+ switch (txrate) {
+ case TX_RATE_1_MBIT:
+ txratename = "1Mbps";
+ break;
+ case TX_RATE_2_MBIT:
+ txratename = "2Mbsp";
+ break;
+ case TX_RATE_5_5_MBIT:
+ txratename = "5.5Mbps";
+ break;
+ case TX_RATE_11_MBIT:
+ txratename = "11Mbps";
+ break;
+ default:
+ IPW_DEBUG_INFO("Unknown rate: %d\n", txrate);
+ txratename = "unknown rate";
+ break;
+ }
+
+ IPW_DEBUG_INFO("%s: Associated with '%s' at %s, channel %d (BSSID="
+ MAC_FMT ")\n",
+ priv->net_dev->name, escape_essid(essid, essid_len),
+ txratename, chan, MAC_ARG(bssid));
+
+ /* now we copy read ssid into dev */
+ if (!(priv->config & CFG_STATIC_ESSID)) {
+ priv->essid_len = min((u8)essid_len, (u8)IW_ESSID_MAX_SIZE);
+ memcpy(priv->essid, essid, priv->essid_len);
+ }
+ priv->channel = chan;
+ memcpy(priv->bssid, bssid, ETH_ALEN);
+
+ priv->status |= STATUS_ASSOCIATING;
+ priv->connect_start = get_seconds();
+
+ queue_delayed_work(priv->workqueue, &priv->wx_event_work, HZ / 10);
+}
+
+
+static int ipw2100_set_essid(struct ipw2100_priv *priv, char *essid,
+ int length, int batch_mode)
+{
+ int ssid_len = min(length, IW_ESSID_MAX_SIZE);
+ struct host_command cmd = {
+ .host_command = SSID,
+ .host_command_sequence = 0,
+ .host_command_length = ssid_len
+ };
+ int err;
+
+ IPW_DEBUG_HC("SSID: '%s'\n", escape_essid(essid, ssid_len));
+
+ if (ssid_len)
+ memcpy((char*)cmd.host_command_parameters,
+ essid, ssid_len);
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ /* Bug in FW currently doesn't honor bit 0 in SET_SCAN_OPTIONS to
+ * disable auto association -- so we cheat by setting a bogus SSID */
+ if (!ssid_len && !(priv->config & CFG_ASSOCIATE)) {
+ int i;
+ u8 *bogus = (u8*)cmd.host_command_parameters;
+ for (i = 0; i < IW_ESSID_MAX_SIZE; i++)
+ bogus[i] = 0x18 + i;
+ cmd.host_command_length = IW_ESSID_MAX_SIZE;
+ }
+
+ /* NOTE: We always send the SSID command even if the provided ESSID is
+ * the same as what we currently think is set. */
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (!err) {
+ memset(priv->essid + ssid_len, 0,
+ IW_ESSID_MAX_SIZE - ssid_len);
+ memcpy(priv->essid, essid, ssid_len);
+ priv->essid_len = ssid_len;
+ }
+
+ if (!batch_mode) {
+ if (ipw2100_enable_adapter(priv))
+ err = -EIO;
+ }
+
+ return err;
+}
+
+static void isr_indicate_association_lost(struct ipw2100_priv *priv, u32 status)
+{
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "disassociated: '%s' " MAC_FMT " \n",
+ escape_essid(priv->essid, priv->essid_len),
+ MAC_ARG(priv->bssid));
+
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+
+ if (priv->status & STATUS_STOPPING) {
+ IPW_DEBUG_INFO("Card is stopping itself, discard ASSN_LOST.\n");
+ return;
+ }
+
+ memset(priv->bssid, 0, ETH_ALEN);
+ memset(priv->ieee->bssid, 0, ETH_ALEN);
+
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+
+ if (!(priv->status & STATUS_RUNNING))
+ return;
+
+ if (priv->status & STATUS_SECURITY_UPDATED)
+ queue_work(priv->workqueue, &priv->security_work);
+
+ queue_work(priv->workqueue, &priv->wx_event_work);
+}
+
+static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status)
+{
+ IPW_DEBUG_INFO("%s: RF Kill state changed to radio OFF.\n",
+ priv->net_dev->name);
+
+ /* RF_KILL is now enabled (else we wouldn't be here) */
+ priv->status |= STATUS_RF_KILL_HW;
+
+#ifdef ACPI_CSTATE_LIMIT_DEFINED
+ if (priv->config & CFG_C3_DISABLED) {
+ IPW_DEBUG_INFO(DRV_NAME ": Resetting C3 transitions.\n");
+ acpi_set_cstate_limit(priv->cstate_limit);
+ priv->config &= ~CFG_C3_DISABLED;
+ }
+#endif
+
+ /* Make sure the RF Kill check timer is running */
+ priv->stop_rf_kill = 0;
+ cancel_delayed_work(&priv->rf_kill);
+ queue_delayed_work(priv->workqueue, &priv->rf_kill, HZ);
+}
+
+static void isr_scan_complete(struct ipw2100_priv *priv, u32 status)
+{
+ IPW_DEBUG_SCAN("scan complete\n");
+ /* Age the scan results... */
+ priv->ieee->scans++;
+ priv->status &= ~STATUS_SCANNING;
+}
+
+#ifdef CONFIG_IPW_DEBUG
+#define IPW2100_HANDLER(v, f) { v, f, # v }
+struct ipw2100_status_indicator {
+ int status;
+ void (*cb)(struct ipw2100_priv *priv, u32 status);
+ char *name;
+};
+#else
+#define IPW2100_HANDLER(v, f) { v, f }
+struct ipw2100_status_indicator {
+ int status;
+ void (*cb)(struct ipw2100_priv *priv, u32 status);
+};
+#endif /* CONFIG_IPW_DEBUG */
+
+static void isr_indicate_scanning(struct ipw2100_priv *priv, u32 status)
+{
+ IPW_DEBUG_SCAN("Scanning...\n");
+ priv->status |= STATUS_SCANNING;
+}
+
+static const struct ipw2100_status_indicator status_handlers[] = {
+ IPW2100_HANDLER(IPW_STATE_INITIALIZED, 0),
+ IPW2100_HANDLER(IPW_STATE_COUNTRY_FOUND, 0),
+ IPW2100_HANDLER(IPW_STATE_ASSOCIATED, isr_indicate_associated),
+ IPW2100_HANDLER(IPW_STATE_ASSN_LOST, isr_indicate_association_lost),
+ IPW2100_HANDLER(IPW_STATE_ASSN_CHANGED, 0),
+ IPW2100_HANDLER(IPW_STATE_SCAN_COMPLETE, isr_scan_complete),
+ IPW2100_HANDLER(IPW_STATE_ENTERED_PSP, 0),
+ IPW2100_HANDLER(IPW_STATE_LEFT_PSP, 0),
+ IPW2100_HANDLER(IPW_STATE_RF_KILL, isr_indicate_rf_kill),
+ IPW2100_HANDLER(IPW_STATE_DISABLED, 0),
+ IPW2100_HANDLER(IPW_STATE_POWER_DOWN, 0),
+ IPW2100_HANDLER(IPW_STATE_SCANNING, isr_indicate_scanning),
+ IPW2100_HANDLER(-1, 0)
+};
+
+
+static void isr_status_change(struct ipw2100_priv *priv, int status)
+{
+ int i;
+
+ if (status == IPW_STATE_SCANNING &&
+ priv->status & STATUS_ASSOCIATED &&
+ !(priv->status & STATUS_SCANNING)) {
+ IPW_DEBUG_INFO("Scan detected while associated, with "
+ "no scan request. Restarting firmware.\n");
+
+ /* Wake up any sleeping jobs */
+ schedule_reset(priv);
+ }
+
+ for (i = 0; status_handlers[i].status != -1; i++) {
+ if (status == status_handlers[i].status) {
+ IPW_DEBUG_NOTIF("Status change: %s\n",
+ status_handlers[i].name);
+ if (status_handlers[i].cb)
+ status_handlers[i].cb(priv, status);
+ priv->wstats.status = status;
+ return;
+ }
+ }
+
+ IPW_DEBUG_NOTIF("unknown status received: %04x\n", status);
+}
+
+static void isr_rx_complete_command(
+ struct ipw2100_priv *priv,
+ struct ipw2100_cmd_header *cmd)
+{
+#ifdef CONFIG_IPW_DEBUG
+ if (cmd->host_command_reg < ARRAY_SIZE(command_types)) {
+ IPW_DEBUG_HC("Command completed '%s (%d)'\n",
+ command_types[cmd->host_command_reg],
+ cmd->host_command_reg);
+ }
+#endif
+ if (cmd->host_command_reg == HOST_COMPLETE)
+ priv->status |= STATUS_ENABLED;
+
+ if (cmd->host_command_reg == CARD_DISABLE)
+ priv->status &= ~STATUS_ENABLED;
+
+ priv->status &= ~STATUS_CMD_ACTIVE;
+
+ wake_up_interruptible(&priv->wait_command_queue);
+}
+
+#ifdef CONFIG_IPW_DEBUG
+static const char *frame_types[] = {
+ "COMMAND_STATUS_VAL",
+ "STATUS_CHANGE_VAL",
+ "P80211_DATA_VAL",
+ "P8023_DATA_VAL",
+ "HOST_NOTIFICATION_VAL"
+};
+#endif
+
+
+static inline int ipw2100_alloc_skb(
+ struct ipw2100_priv *priv,
+ struct ipw2100_rx_packet *packet)
+{
+ packet->skb = dev_alloc_skb(sizeof(struct ipw2100_rx));
+ if (!packet->skb)
+ return -ENOMEM;
+
+ packet->rxp = (struct ipw2100_rx *)packet->skb->data;
+ packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data,
+ sizeof(struct ipw2100_rx),
+ PCI_DMA_FROMDEVICE);
+ /* NOTE: pci_map_single does not return an error code, and 0 is a valid
+ * dma_addr */
+
+ return 0;
+}
+
+
+#define SEARCH_ERROR 0xffffffff
+#define SEARCH_FAIL 0xfffffffe
+#define SEARCH_SUCCESS 0xfffffff0
+#define SEARCH_DISCARD 0
+#define SEARCH_SNAPSHOT 1
+
+#define SNAPSHOT_ADDR(ofs) (priv->snapshot[((ofs) >> 12) & 0xff] + ((ofs) & 0xfff))
+static inline int ipw2100_snapshot_alloc(struct ipw2100_priv *priv)
+{
+ int i;
+ if (priv->snapshot[0])
+ return 1;
+ for (i = 0; i < 0x30; i++) {
+ priv->snapshot[i] = (u8*)kmalloc(0x1000, GFP_ATOMIC);
+ if (!priv->snapshot[i]) {
+ IPW_DEBUG_INFO("%s: Error allocating snapshot "
+ "buffer %d\n", priv->net_dev->name, i);
+ while (i > 0)
+ kfree(priv->snapshot[--i]);
+ priv->snapshot[0] = NULL;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static inline void ipw2100_snapshot_free(struct ipw2100_priv *priv)
+{
+ int i;
+ if (!priv->snapshot[0])
+ return;
+ for (i = 0; i < 0x30; i++)
+ kfree(priv->snapshot[i]);
+ priv->snapshot[0] = NULL;
+}
+
+static inline u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 *in_buf,
+ size_t len, int mode)
+{
+ u32 i, j;
+ u32 tmp;
+ u8 *s, *d;
+ u32 ret;
+
+ s = in_buf;
+ if (mode == SEARCH_SNAPSHOT) {
+ if (!ipw2100_snapshot_alloc(priv))
+ mode = SEARCH_DISCARD;
+ }
+
+ for (ret = SEARCH_FAIL, i = 0; i < 0x30000; i += 4) {
+ read_nic_dword(priv->net_dev, i, &tmp);
+ if (mode == SEARCH_SNAPSHOT)
+ *(u32 *)SNAPSHOT_ADDR(i) = tmp;
+ if (ret == SEARCH_FAIL) {
+ d = (u8*)&tmp;
+ for (j = 0; j < 4; j++) {
+ if (*s != *d) {
+ s = in_buf;
+ continue;
+ }
+
+ s++;
+ d++;
+
+ if ((s - in_buf) == len)
+ ret = (i + j) - len + 1;
+ }
+ } else if (mode == SEARCH_DISCARD)
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ *
+ * 0) Disconnect the SKB from the firmware (just unmap)
+ * 1) Pack the ETH header into the SKB
+ * 2) Pass the SKB to the network stack
+ *
+ * When packet is provided by the firmware, it contains the following:
+ *
+ * . ieee80211_hdr
+ * . ieee80211_snap_hdr
+ *
+ * The size of the constructed ethernet
+ *
+ */
+#ifdef CONFIG_IPW2100_RX_DEBUG
+static u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH];
+#endif
+
+static inline void ipw2100_corruption_detected(struct ipw2100_priv *priv,
+ int i)
+{
+#ifdef CONFIG_IPW_DEBUG_C3
+ struct ipw2100_status *status = &priv->status_queue.drv[i];
+ u32 match, reg;
+ int j;
+#endif
+#ifdef ACPI_CSTATE_LIMIT_DEFINED
+ int limit;
+#endif
+
+ IPW_DEBUG_INFO(DRV_NAME ": PCI latency error detected at "
+ "0x%04zX.\n", i * sizeof(struct ipw2100_status));
+
+#ifdef ACPI_CSTATE_LIMIT_DEFINED
+ IPW_DEBUG_INFO(DRV_NAME ": Disabling C3 transitions.\n");
+ limit = acpi_get_cstate_limit();
+ if (limit > 2) {
+ priv->cstate_limit = limit;
+ acpi_set_cstate_limit(2);
+ priv->config |= CFG_C3_DISABLED;
+ }
+#endif
+
+#ifdef CONFIG_IPW_DEBUG_C3
+ /* Halt the fimrware so we can get a good image */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+ j = 5;
+ do {
+ udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+ if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+ break;
+ } while (j--);
+
+ match = ipw2100_match_buf(priv, (u8*)status,
+ sizeof(struct ipw2100_status),
+ SEARCH_SNAPSHOT);
+ if (match < SEARCH_SUCCESS)
+ IPW_DEBUG_INFO("%s: DMA status match in Firmware at "
+ "offset 0x%06X, length %d:\n",
+ priv->net_dev->name, match,
+ sizeof(struct ipw2100_status));
+ else
+ IPW_DEBUG_INFO("%s: No DMA status match in "
+ "Firmware.\n", priv->net_dev->name);
+
+ printk_buf((u8*)priv->status_queue.drv,
+ sizeof(struct ipw2100_status) * RX_QUEUE_LENGTH);
+#endif
+
+ priv->fatal_error = IPW2100_ERR_C3_CORRUPTION;
+ priv->ieee->stats.rx_errors++;
+ schedule_reset(priv);
+}
+
+static inline void isr_rx(struct ipw2100_priv *priv, int i,
+ struct ieee80211_rx_stats *stats)
+{
+ struct ipw2100_status *status = &priv->status_queue.drv[i];
+ struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+ IPW_DEBUG_RX("Handler...\n");
+
+ if (unlikely(status->frame_size > skb_tailroom(packet->skb))) {
+ IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
+ " Dropping.\n",
+ priv->net_dev->name,
+ status->frame_size, skb_tailroom(packet->skb));
+ priv->ieee->stats.rx_errors++;
+ return;
+ }
+
+ if (unlikely(!netif_running(priv->net_dev))) {
+ priv->ieee->stats.rx_errors++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+ return;
+ }
+
+ if (unlikely(priv->ieee->iw_mode == IW_MODE_MONITOR &&
+ status->flags & IPW_STATUS_FLAG_CRC_ERROR)) {
+ IPW_DEBUG_RX("CRC error in packet. Dropping.\n");
+ priv->ieee->stats.rx_errors++;
+ return;
+ }
+
+ if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR &&
+ !(priv->status & STATUS_ASSOCIATED))) {
+ IPW_DEBUG_DROP("Dropping packet while not associated.\n");
+ priv->wstats.discard.misc++;
+ return;
+ }
+
+
+ pci_unmap_single(priv->pci_dev,
+ packet->dma_addr,
+ sizeof(struct ipw2100_rx),
+ PCI_DMA_FROMDEVICE);
+
+ skb_put(packet->skb, status->frame_size);
+
+#ifdef CONFIG_IPW2100_RX_DEBUG
+ /* Make a copy of the frame so we can dump it to the logs if
+ * ieee80211_rx fails */
+ memcpy(packet_data, packet->skb->data,
+ min_t(u32, status->frame_size, IPW_RX_NIC_BUFFER_LENGTH));
+#endif
+
+ if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
+#ifdef CONFIG_IPW2100_RX_DEBUG
+ IPW_DEBUG_DROP("%s: Non consumed packet:\n",
+ priv->net_dev->name);
+ printk_buf(IPW_DL_DROP, packet_data, status->frame_size);
+#endif
+ priv->ieee->stats.rx_errors++;
+
+ /* ieee80211_rx failed, so it didn't free the SKB */
+ dev_kfree_skb_any(packet->skb);
+ packet->skb = NULL;
+ }
+
+ /* We need to allocate a new SKB and attach it to the RDB. */
+ if (unlikely(ipw2100_alloc_skb(priv, packet))) {
+ printk(KERN_WARNING DRV_NAME ": "
+ "%s: Unable to allocate SKB onto RBD ring - disabling "
+ "adapter.\n", priv->net_dev->name);
+ /* TODO: schedule adapter shutdown */
+ IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
+ }
+
+ /* Update the RDB entry */
+ priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+}
+
+static inline int ipw2100_corruption_check(struct ipw2100_priv *priv, int i)
+{
+ struct ipw2100_status *status = &priv->status_queue.drv[i];
+ struct ipw2100_rx *u = priv->rx_buffers[i].rxp;
+ u16 frame_type = status->status_fields & STATUS_TYPE_MASK;
+
+ switch (frame_type) {
+ case COMMAND_STATUS_VAL:
+ return (status->frame_size != sizeof(u->rx_data.command));
+ case STATUS_CHANGE_VAL:
+ return (status->frame_size != sizeof(u->rx_data.status));
+ case HOST_NOTIFICATION_VAL:
+ return (status->frame_size < sizeof(u->rx_data.notification));
+ case P80211_DATA_VAL:
+ case P8023_DATA_VAL:
+#ifdef CONFIG_IPW2100_MONITOR
+ return 0;
+#else
+ switch (WLAN_FC_GET_TYPE(u->rx_data.header.frame_ctl)) {
+ case IEEE80211_FTYPE_MGMT:
+ case IEEE80211_FTYPE_CTL:
+ return 0;
+ case IEEE80211_FTYPE_DATA:
+ return (status->frame_size >
+ IPW_MAX_802_11_PAYLOAD_LENGTH);
+ }
+#endif
+ }
+
+ return 1;
+}
+
+/*
+ * ipw2100 interrupts are disabled at this point, and the ISR
+ * is the only code that calls this method. So, we do not need
+ * to play with any locks.
+ *
+ * RX Queue works as follows:
+ *
+ * Read index - firmware places packet in entry identified by the
+ * Read index and advances Read index. In this manner,
+ * Read index will always point to the next packet to
+ * be filled--but not yet valid.
+ *
+ * Write index - driver fills this entry with an unused RBD entry.
+ * This entry has not filled by the firmware yet.
+ *
+ * In between the W and R indexes are the RBDs that have been received
+ * but not yet processed.
+ *
+ * The process of handling packets will start at WRITE + 1 and advance
+ * until it reaches the READ index.
+ *
+ * The WRITE index is cached in the variable 'priv->rx_queue.next'.
+ *
+ */
+static inline void __ipw2100_rx_process(struct ipw2100_priv *priv)
+{
+ struct ipw2100_bd_queue *rxq = &priv->rx_queue;
+ struct ipw2100_status_queue *sq = &priv->status_queue;
+ struct ipw2100_rx_packet *packet;
+ u16 frame_type;
+ u32 r, w, i, s;
+ struct ipw2100_rx *u;
+ struct ieee80211_rx_stats stats = {
+ .mac_time = jiffies,
+ };
+
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_READ_INDEX, &r);
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, &w);
+
+ if (r >= rxq->entries) {
+ IPW_DEBUG_RX("exit - bad read index\n");
+ return;
+ }
+
+ i = (rxq->next + 1) % rxq->entries;
+ s = i;
+ while (i != r) {
+ /* IPW_DEBUG_RX("r = %d : w = %d : processing = %d\n",
+ r, rxq->next, i); */
+
+ packet = &priv->rx_buffers[i];
+
+ /* Sync the DMA for the STATUS buffer so CPU is sure to get
+ * the correct values */
+ pci_dma_sync_single_for_cpu(
+ priv->pci_dev,
+ sq->nic + sizeof(struct ipw2100_status) * i,
+ sizeof(struct ipw2100_status),
+ PCI_DMA_FROMDEVICE);
+
+ /* Sync the DMA for the RX buffer so CPU is sure to get
+ * the correct values */
+ pci_dma_sync_single_for_cpu(priv->pci_dev, packet->dma_addr,
+ sizeof(struct ipw2100_rx),
+ PCI_DMA_FROMDEVICE);
+
+ if (unlikely(ipw2100_corruption_check(priv, i))) {
+ ipw2100_corruption_detected(priv, i);
+ goto increment;
+ }
+
+ u = packet->rxp;
+ frame_type = sq->drv[i].status_fields &
+ STATUS_TYPE_MASK;
+ stats.rssi = sq->drv[i].rssi + IPW2100_RSSI_TO_DBM;
+ stats.len = sq->drv[i].frame_size;
+
+ stats.mask = 0;
+ if (stats.rssi != 0)
+ stats.mask |= IEEE80211_STATMASK_RSSI;
+ stats.freq = IEEE80211_24GHZ_BAND;
+
+ IPW_DEBUG_RX(
+ "%s: '%s' frame type received (%d).\n",
+ priv->net_dev->name, frame_types[frame_type],
+ stats.len);
+
+ switch (frame_type) {
+ case COMMAND_STATUS_VAL:
+ /* Reset Rx watchdog */
+ isr_rx_complete_command(
+ priv, &u->rx_data.command);
+ break;
+
+ case STATUS_CHANGE_VAL:
+ isr_status_change(priv, u->rx_data.status);
+ break;
+
+ case P80211_DATA_VAL:
+ case P8023_DATA_VAL:
+#ifdef CONFIG_IPW2100_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ isr_rx(priv, i, &stats);
+ break;
+ }
+#endif
+ if (stats.len < sizeof(u->rx_data.header))
+ break;
+ switch (WLAN_FC_GET_TYPE(u->rx_data.header.
+ frame_ctl)) {
+ case IEEE80211_FTYPE_MGMT:
+ ieee80211_rx_mgt(priv->ieee,
+ &u->rx_data.header,
+ &stats);
+ break;
+
+ case IEEE80211_FTYPE_CTL:
+ break;
+
+ case IEEE80211_FTYPE_DATA:
+ isr_rx(priv, i, &stats);
+ break;
+
+ }
+ break;
+ }
+
+ increment:
+ /* clear status field associated with this RBD */
+ rxq->drv[i].status.info.field = 0;
+
+ i = (i + 1) % rxq->entries;
+ }
+
+ if (i != s) {
+ /* backtrack one entry, wrapping to end if at 0 */
+ rxq->next = (i ? i : rxq->entries) - 1;
+
+ write_register(priv->net_dev,
+ IPW_MEM_HOST_SHARED_RX_WRITE_INDEX,
+ rxq->next);
+ }
+}
+
+
+/*
+ * __ipw2100_tx_process
+ *
+ * This routine will determine whether the next packet on
+ * the fw_pend_list has been processed by the firmware yet.
+ *
+ * If not, then it does nothing and returns.
+ *
+ * If so, then it removes the item from the fw_pend_list, frees
+ * any associated storage, and places the item back on the
+ * free list of its source (either msg_free_list or tx_free_list)
+ *
+ * TX Queue works as follows:
+ *
+ * Read index - points to the next TBD that the firmware will
+ * process. The firmware will read the data, and once
+ * done processing, it will advance the Read index.
+ *
+ * Write index - driver fills this entry with an constructed TBD
+ * entry. The Write index is not advanced until the
+ * packet has been configured.
+ *
+ * In between the W and R indexes are the TBDs that have NOT been
+ * processed. Lagging behind the R index are packets that have
+ * been processed but have not been freed by the driver.
+ *
+ * In order to free old storage, an internal index will be maintained
+ * that points to the next packet to be freed. When all used
+ * packets have been freed, the oldest index will be the same as the
+ * firmware's read index.
+ *
+ * The OLDEST index is cached in the variable 'priv->tx_queue.oldest'
+ *
+ * Because the TBD structure can not contain arbitrary data, the
+ * driver must keep an internal queue of cached allocations such that
+ * it can put that data back into the tx_free_list and msg_free_list
+ * for use by future command and data packets.
+ *
+ */
+static inline int __ipw2100_tx_process(struct ipw2100_priv *priv)
+{
+ struct ipw2100_bd_queue *txq = &priv->tx_queue;
+ struct ipw2100_bd *tbd;
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ int descriptors_used;
+ int e, i;
+ u32 r, w, frag_num = 0;
+
+ if (list_empty(&priv->fw_pend_list))
+ return 0;
+
+ element = priv->fw_pend_list.next;
+
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+ tbd = &txq->drv[packet->index];
+
+ /* Determine how many TBD entries must be finished... */
+ switch (packet->type) {
+ case COMMAND:
+ /* COMMAND uses only one slot; don't advance */
+ descriptors_used = 1;
+ e = txq->oldest;
+ break;
+
+ case DATA:
+ /* DATA uses two slots; advance and loop position. */
+ descriptors_used = tbd->num_fragments;
+ frag_num = tbd->num_fragments - 1;
+ e = txq->oldest + frag_num;
+ e %= txq->entries;
+ break;
+
+ default:
+ printk(KERN_WARNING DRV_NAME ": %s: Bad fw_pend_list entry!\n",
+ priv->net_dev->name);
+ return 0;
+ }
+
+ /* if the last TBD is not done by NIC yet, then packet is
+ * not ready to be released.
+ *
+ */
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
+ &r);
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+ &w);
+ if (w != txq->next)
+ printk(KERN_WARNING DRV_NAME ": %s: write index mismatch\n",
+ priv->net_dev->name);
+
+ /*
+ * txq->next is the index of the last packet written txq->oldest is
+ * the index of the r is the index of the next packet to be read by
+ * firmware
+ */
+
+
+ /*
+ * Quick graphic to help you visualize the following
+ * if / else statement
+ *
+ * ===>| s---->|===============
+ * e>|
+ * | a | b | c | d | e | f | g | h | i | j | k | l
+ * r---->|
+ * w
+ *
+ * w - updated by driver
+ * r - updated by firmware
+ * s - start of oldest BD entry (txq->oldest)
+ * e - end of oldest BD entry
+ *
+ */
+ if (!((r <= w && (e < r || e >= w)) || (e < r && e >= w))) {
+ IPW_DEBUG_TX("exit - no processed packets ready to release.\n");
+ return 0;
+ }
+
+ list_del(element);
+ DEC_STAT(&priv->fw_pend_stat);
+
+#ifdef CONFIG_IPW_DEBUG
+ {
+ int i = txq->oldest;
+ IPW_DEBUG_TX(
+ "TX%d V=%p P=%04X T=%04X L=%d\n", i,
+ &txq->drv[i],
+ (u32)(txq->nic + i * sizeof(struct ipw2100_bd)),
+ txq->drv[i].host_addr,
+ txq->drv[i].buf_length);
+
+ if (packet->type == DATA) {
+ i = (i + 1) % txq->entries;
+
+ IPW_DEBUG_TX(
+ "TX%d V=%p P=%04X T=%04X L=%d\n", i,
+ &txq->drv[i],
+ (u32)(txq->nic + i *
+ sizeof(struct ipw2100_bd)),
+ (u32)txq->drv[i].host_addr,
+ txq->drv[i].buf_length);
+ }
+ }
+#endif
+
+ switch (packet->type) {
+ case DATA:
+ if (txq->drv[txq->oldest].status.info.fields.txType != 0)
+ printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. "
+ "Expecting DATA TBD but pulled "
+ "something else: ids %d=%d.\n",
+ priv->net_dev->name, txq->oldest, packet->index);
+
+ /* DATA packet; we have to unmap and free the SKB */
+ priv->ieee->stats.tx_packets++;
+ for (i = 0; i < frag_num; i++) {
+ tbd = &txq->drv[(packet->index + 1 + i) %
+ txq->entries];
+
+ IPW_DEBUG_TX(
+ "TX%d P=%08x L=%d\n",
+ (packet->index + 1 + i) % txq->entries,
+ tbd->host_addr, tbd->buf_length);
+
+ pci_unmap_single(priv->pci_dev,
+ tbd->host_addr,
+ tbd->buf_length,
+ PCI_DMA_TODEVICE);
+ }
+
+ priv->ieee->stats.tx_bytes += packet->info.d_struct.txb->payload_size;
+ ieee80211_txb_free(packet->info.d_struct.txb);
+ packet->info.d_struct.txb = NULL;
+
+ list_add_tail(element, &priv->tx_free_list);
+ INC_STAT(&priv->tx_free_stat);
+
+ /* We have a free slot in the Tx queue, so wake up the
+ * transmit layer if it is stopped. */
+ if (priv->status & STATUS_ASSOCIATED &&
+ netif_queue_stopped(priv->net_dev)) {
+ IPW_DEBUG_INFO(KERN_INFO
+ "%s: Waking net queue.\n",
+ priv->net_dev->name);
+ netif_wake_queue(priv->net_dev);
+ }
+
+ /* A packet was processed by the hardware, so update the
+ * watchdog */
+ priv->net_dev->trans_start = jiffies;
+
+ break;
+
+ case COMMAND:
+ if (txq->drv[txq->oldest].status.info.fields.txType != 1)
+ printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. "
+ "Expecting COMMAND TBD but pulled "
+ "something else: ids %d=%d.\n",
+ priv->net_dev->name, txq->oldest, packet->index);
+
+#ifdef CONFIG_IPW_DEBUG
+ if (packet->info.c_struct.cmd->host_command_reg <
+ sizeof(command_types) / sizeof(*command_types))
+ IPW_DEBUG_TX(
+ "Command '%s (%d)' processed: %d.\n",
+ command_types[packet->info.c_struct.cmd->host_command_reg],
+ packet->info.c_struct.cmd->host_command_reg,
+ packet->info.c_struct.cmd->cmd_status_reg);
+#endif
+
+ list_add_tail(element, &priv->msg_free_list);
+ INC_STAT(&priv->msg_free_stat);
+ break;
+ }
+
+ /* advance oldest used TBD pointer to start of next entry */
+ txq->oldest = (e + 1) % txq->entries;
+ /* increase available TBDs number */
+ txq->available += descriptors_used;
+ SET_STAT(&priv->txq_stat, txq->available);
+
+ IPW_DEBUG_TX("packet latency (send to process) %ld jiffies\n",
+ jiffies - packet->jiffy_start);
+
+ return (!list_empty(&priv->fw_pend_list));
+}
+
+
+static inline void __ipw2100_tx_complete(struct ipw2100_priv *priv)
+{
+ int i = 0;
+
+ while (__ipw2100_tx_process(priv) && i < 200) i++;
+
+ if (i == 200) {
+ printk(KERN_WARNING DRV_NAME ": "
+ "%s: Driver is running slow (%d iters).\n",
+ priv->net_dev->name, i);
+ }
+}
+
+
+static void ipw2100_tx_send_commands(struct ipw2100_priv *priv)
+{
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ struct ipw2100_bd_queue *txq = &priv->tx_queue;
+ struct ipw2100_bd *tbd;
+ int next = txq->next;
+
+ while (!list_empty(&priv->msg_pend_list)) {
+ /* if there isn't enough space in TBD queue, then
+ * don't stuff a new one in.
+ * NOTE: 3 are needed as a command will take one,
+ * and there is a minimum of 2 that must be
+ * maintained between the r and w indexes
+ */
+ if (txq->available <= 3) {
+ IPW_DEBUG_TX("no room in tx_queue\n");
+ break;
+ }
+
+ element = priv->msg_pend_list.next;
+ list_del(element);
+ DEC_STAT(&priv->msg_pend_stat);
+
+ packet = list_entry(element,
+ struct ipw2100_tx_packet, list);
+
+ IPW_DEBUG_TX("using TBD at virt=%p, phys=%p\n",
+ &txq->drv[txq->next],
+ (void*)(txq->nic + txq->next *
+ sizeof(struct ipw2100_bd)));
+
+ packet->index = txq->next;
+
+ tbd = &txq->drv[txq->next];
+
+ /* initialize TBD */
+ tbd->host_addr = packet->info.c_struct.cmd_phys;
+ tbd->buf_length = sizeof(struct ipw2100_cmd_header);
+ /* not marking number of fragments causes problems
+ * with f/w debug version */
+ tbd->num_fragments = 1;
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_COMMAND |
+ IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
+
+ /* update TBD queue counters */
+ txq->next++;
+ txq->next %= txq->entries;
+ txq->available--;
+ DEC_STAT(&priv->txq_stat);
+
+ list_add_tail(element, &priv->fw_pend_list);
+ INC_STAT(&priv->fw_pend_stat);
+ }
+
+ if (txq->next != next) {
+ /* kick off the DMA by notifying firmware the
+ * write index has moved; make sure TBD stores are sync'd */
+ wmb();
+ write_register(priv->net_dev,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+ txq->next);
+ }
+}
+
+
+/*
+ * ipw2100_tx_send_data
+ *
+ */
+static void ipw2100_tx_send_data(struct ipw2100_priv *priv)
+{
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ struct ipw2100_bd_queue *txq = &priv->tx_queue;
+ struct ipw2100_bd *tbd;
+ int next = txq->next;
+ int i = 0;
+ struct ipw2100_data_header *ipw_hdr;
+ struct ieee80211_hdr *hdr;
+
+ while (!list_empty(&priv->tx_pend_list)) {
+ /* if there isn't enough space in TBD queue, then
+ * don't stuff a new one in.
+ * NOTE: 4 are needed as a data will take two,
+ * and there is a minimum of 2 that must be
+ * maintained between the r and w indexes
+ */
+ element = priv->tx_pend_list.next;
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+ if (unlikely(1 + packet->info.d_struct.txb->nr_frags >
+ IPW_MAX_BDS)) {
+ /* TODO: Support merging buffers if more than
+ * IPW_MAX_BDS are used */
+ IPW_DEBUG_INFO(
+ "%s: Maximum BD theshold exceeded. "
+ "Increase fragmentation level.\n",
+ priv->net_dev->name);
+ }
+
+ if (txq->available <= 3 +
+ packet->info.d_struct.txb->nr_frags) {
+ IPW_DEBUG_TX("no room in tx_queue\n");
+ break;
+ }
+
+ list_del(element);
+ DEC_STAT(&priv->tx_pend_stat);
+
+ tbd = &txq->drv[txq->next];
+
+ packet->index = txq->next;
+
+ ipw_hdr = packet->info.d_struct.data;
+ hdr = (struct ieee80211_hdr *)packet->info.d_struct.txb->
+ fragments[0]->data;
+
+ if (priv->ieee->iw_mode == IW_MODE_INFRA) {
+ /* To DS: Addr1 = BSSID, Addr2 = SA,
+ Addr3 = DA */
+ memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
+ memcpy(ipw_hdr->dst_addr, hdr->addr3, ETH_ALEN);
+ } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ /* not From/To DS: Addr1 = DA, Addr2 = SA,
+ Addr3 = BSSID */
+ memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
+ memcpy(ipw_hdr->dst_addr, hdr->addr1, ETH_ALEN);
+ }
+
+ ipw_hdr->host_command_reg = SEND;
+ ipw_hdr->host_command_reg1 = 0;
+
+ /* For now we only support host based encryption */
+ ipw_hdr->needs_encryption = 0;
+ ipw_hdr->encrypted = packet->info.d_struct.txb->encrypted;
+ if (packet->info.d_struct.txb->nr_frags > 1)
+ ipw_hdr->fragment_size =
+ packet->info.d_struct.txb->frag_size - IEEE80211_3ADDR_LEN;
+ else
+ ipw_hdr->fragment_size = 0;
+
+ tbd->host_addr = packet->info.d_struct.data_phys;
+ tbd->buf_length = sizeof(struct ipw2100_data_header);
+ tbd->num_fragments = 1 + packet->info.d_struct.txb->nr_frags;
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_802_3 |
+ IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
+ txq->next++;
+ txq->next %= txq->entries;
+
+ IPW_DEBUG_TX(
+ "data header tbd TX%d P=%08x L=%d\n",
+ packet->index, tbd->host_addr,
+ tbd->buf_length);
+#ifdef CONFIG_IPW_DEBUG
+ if (packet->info.d_struct.txb->nr_frags > 1)
+ IPW_DEBUG_FRAG("fragment Tx: %d frames\n",
+ packet->info.d_struct.txb->nr_frags);
+#endif
+
+ for (i = 0; i < packet->info.d_struct.txb->nr_frags; i++) {
+ tbd = &txq->drv[txq->next];
+ if (i == packet->info.d_struct.txb->nr_frags - 1)
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_802_3 |
+ IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
+ else
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_802_3 |
+ IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
+
+ tbd->buf_length = packet->info.d_struct.txb->
+ fragments[i]->len - IEEE80211_3ADDR_LEN;
+
+ tbd->host_addr = pci_map_single(
+ priv->pci_dev,
+ packet->info.d_struct.txb->fragments[i]->data +
+ IEEE80211_3ADDR_LEN,
+ tbd->buf_length,
+ PCI_DMA_TODEVICE);
+
+ IPW_DEBUG_TX(
+ "data frag tbd TX%d P=%08x L=%d\n",
+ txq->next, tbd->host_addr, tbd->buf_length);
+
+ pci_dma_sync_single_for_device(
+ priv->pci_dev, tbd->host_addr,
+ tbd->buf_length,
+ PCI_DMA_TODEVICE);
+
+ txq->next++;
+ txq->next %= txq->entries;
+ }
+
+ txq->available -= 1 + packet->info.d_struct.txb->nr_frags;
+ SET_STAT(&priv->txq_stat, txq->available);
+
+ list_add_tail(element, &priv->fw_pend_list);
+ INC_STAT(&priv->fw_pend_stat);
+ }
+
+ if (txq->next != next) {
+ /* kick off the DMA by notifying firmware the
+ * write index has moved; make sure TBD stores are sync'd */
+ write_register(priv->net_dev,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+ txq->next);
+ }
+ return;
+}
+
+static void ipw2100_irq_tasklet(struct ipw2100_priv *priv)
+{
+ struct net_device *dev = priv->net_dev;
+ unsigned long flags;
+ u32 inta, tmp;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_disable_interrupts(priv);
+
+ read_register(dev, IPW_REG_INTA, &inta);
+
+ IPW_DEBUG_ISR("enter - INTA: 0x%08lX\n",
+ (unsigned long)inta & IPW_INTERRUPT_MASK);
+
+ priv->in_isr++;
+ priv->interrupts++;
+
+ /* We do not loop and keep polling for more interrupts as this
+ * is frowned upon and doesn't play nicely with other potentially
+ * chained IRQs */
+ IPW_DEBUG_ISR("INTA: 0x%08lX\n",
+ (unsigned long)inta & IPW_INTERRUPT_MASK);
+
+ if (inta & IPW2100_INTA_FATAL_ERROR) {
+ printk(KERN_WARNING DRV_NAME
+ ": Fatal interrupt. Scheduling firmware restart.\n");
+ priv->inta_other++;
+ write_register(
+ dev, IPW_REG_INTA,
+ IPW2100_INTA_FATAL_ERROR);
+
+ read_nic_dword(dev, IPW_NIC_FATAL_ERROR, &priv->fatal_error);
+ IPW_DEBUG_INFO("%s: Fatal error value: 0x%08X\n",
+ priv->net_dev->name, priv->fatal_error);
+
+ read_nic_dword(dev, IPW_ERROR_ADDR(priv->fatal_error), &tmp);
+ IPW_DEBUG_INFO("%s: Fatal error address value: 0x%08X\n",
+ priv->net_dev->name, tmp);
+
+ /* Wake up any sleeping jobs */
+ schedule_reset(priv);
+ }
+
+ if (inta & IPW2100_INTA_PARITY_ERROR) {
+ printk(KERN_ERR DRV_NAME ": ***** PARITY ERROR INTERRUPT !!!! \n");
+ priv->inta_other++;
+ write_register(
+ dev, IPW_REG_INTA,
+ IPW2100_INTA_PARITY_ERROR);
+ }
+
+ if (inta & IPW2100_INTA_RX_TRANSFER) {
+ IPW_DEBUG_ISR("RX interrupt\n");
+
+ priv->rx_interrupts++;
+
+ write_register(
+ dev, IPW_REG_INTA,
+ IPW2100_INTA_RX_TRANSFER);
+
+ __ipw2100_rx_process(priv);
+ __ipw2100_tx_complete(priv);
+ }
+
+ if (inta & IPW2100_INTA_TX_TRANSFER) {
+ IPW_DEBUG_ISR("TX interrupt\n");
+
+ priv->tx_interrupts++;
+
+ write_register(dev, IPW_REG_INTA,
+ IPW2100_INTA_TX_TRANSFER);
+
+ __ipw2100_tx_complete(priv);
+ ipw2100_tx_send_commands(priv);
+ ipw2100_tx_send_data(priv);
+ }
+
+ if (inta & IPW2100_INTA_TX_COMPLETE) {
+ IPW_DEBUG_ISR("TX complete\n");
+ priv->inta_other++;
+ write_register(
+ dev, IPW_REG_INTA,
+ IPW2100_INTA_TX_COMPLETE);
+
+ __ipw2100_tx_complete(priv);
+ }
+
+ if (inta & IPW2100_INTA_EVENT_INTERRUPT) {
+ /* ipw2100_handle_event(dev); */
+ priv->inta_other++;
+ write_register(
+ dev, IPW_REG_INTA,
+ IPW2100_INTA_EVENT_INTERRUPT);
+ }
+
+ if (inta & IPW2100_INTA_FW_INIT_DONE) {
+ IPW_DEBUG_ISR("FW init done interrupt\n");
+ priv->inta_other++;
+
+ read_register(dev, IPW_REG_INTA, &tmp);
+ if (tmp & (IPW2100_INTA_FATAL_ERROR |
+ IPW2100_INTA_PARITY_ERROR)) {
+ write_register(
+ dev, IPW_REG_INTA,
+ IPW2100_INTA_FATAL_ERROR |
+ IPW2100_INTA_PARITY_ERROR);
+ }
+
+ write_register(dev, IPW_REG_INTA,
+ IPW2100_INTA_FW_INIT_DONE);
+ }
+
+ if (inta & IPW2100_INTA_STATUS_CHANGE) {
+ IPW_DEBUG_ISR("Status change interrupt\n");
+ priv->inta_other++;
+ write_register(
+ dev, IPW_REG_INTA,
+ IPW2100_INTA_STATUS_CHANGE);
+ }
+
+ if (inta & IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE) {
+ IPW_DEBUG_ISR("slave host mode interrupt\n");
+ priv->inta_other++;
+ write_register(
+ dev, IPW_REG_INTA,
+ IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE);
+ }
+
+ priv->in_isr--;
+ ipw2100_enable_interrupts(priv);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ IPW_DEBUG_ISR("exit\n");
+}
+
+
+static irqreturn_t ipw2100_interrupt(int irq, void *data,
+ struct pt_regs *regs)
+{
+ struct ipw2100_priv *priv = data;
+ u32 inta, inta_mask;
+
+ if (!data)
+ return IRQ_NONE;
+
+ spin_lock(&priv->low_lock);
+
+ /* We check to see if we should be ignoring interrupts before
+ * we touch the hardware. During ucode load if we try and handle
+ * an interrupt we can cause keyboard problems as well as cause
+ * the ucode to fail to initialize */
+ if (!(priv->status & STATUS_INT_ENABLED)) {
+ /* Shared IRQ */
+ goto none;
+ }
+
+ read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
+ read_register(priv->net_dev, IPW_REG_INTA, &inta);
+
+ if (inta == 0xFFFFFFFF) {
+ /* Hardware disappeared */
+ printk(KERN_WARNING DRV_NAME ": IRQ INTA == 0xFFFFFFFF\n");
+ goto none;
+ }
+
+ inta &= IPW_INTERRUPT_MASK;
+
+ if (!(inta & inta_mask)) {
+ /* Shared interrupt */
+ goto none;
+ }
+
+ /* We disable the hardware interrupt here just to prevent unneeded
+ * calls to be made. We disable this again within the actual
+ * work tasklet, so if another part of the code re-enables the
+ * interrupt, that is fine */
+ ipw2100_disable_interrupts(priv);
+
+ tasklet_schedule(&priv->irq_tasklet);
+ spin_unlock(&priv->low_lock);
+
+ return IRQ_HANDLED;
+ none:
+ spin_unlock(&priv->low_lock);
+ return IRQ_NONE;
+}
+
+static int ipw2100_tx(struct ieee80211_txb *txb, struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ IPW_DEBUG_INFO("Can not transmit when not connected.\n");
+ priv->ieee->stats.tx_carrier_errors++;
+ netif_stop_queue(dev);
+ goto fail_unlock;
+ }
+
+ if (list_empty(&priv->tx_free_list))
+ goto fail_unlock;
+
+ element = priv->tx_free_list.next;
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+ packet->info.d_struct.txb = txb;
+
+ IPW_DEBUG_TX("Sending fragment (%d bytes):\n",
+ txb->fragments[0]->len);
+ printk_buf(IPW_DL_TX, txb->fragments[0]->data,
+ txb->fragments[0]->len);
+
+ packet->jiffy_start = jiffies;
+
+ list_del(element);
+ DEC_STAT(&priv->tx_free_stat);
+
+ list_add_tail(element, &priv->tx_pend_list);
+ INC_STAT(&priv->tx_pend_stat);
+
+ ipw2100_tx_send_data(priv);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+ return 0;
+
+ fail_unlock:
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+ return 1;
+}
+
+
+static int ipw2100_msg_allocate(struct ipw2100_priv *priv)
+{
+ int i, j, err = -EINVAL;
+ void *v;
+ dma_addr_t p;
+
+ priv->msg_buffers = (struct ipw2100_tx_packet *)kmalloc(
+ IPW_COMMAND_POOL_SIZE * sizeof(struct ipw2100_tx_packet),
+ GFP_KERNEL);
+ if (!priv->msg_buffers) {
+ printk(KERN_ERR DRV_NAME ": %s: PCI alloc failed for msg "
+ "buffers.\n", priv->net_dev->name);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
+ v = pci_alloc_consistent(
+ priv->pci_dev,
+ sizeof(struct ipw2100_cmd_header),
+ &p);
+ if (!v) {
+ printk(KERN_ERR DRV_NAME ": "
+ "%s: PCI alloc failed for msg "
+ "buffers.\n",
+ priv->net_dev->name);
+ err = -ENOMEM;
+ break;
+ }
+
+ memset(v, 0, sizeof(struct ipw2100_cmd_header));
+
+ priv->msg_buffers[i].type = COMMAND;
+ priv->msg_buffers[i].info.c_struct.cmd =
+ (struct ipw2100_cmd_header*)v;
+ priv->msg_buffers[i].info.c_struct.cmd_phys = p;
+ }
+
+ if (i == IPW_COMMAND_POOL_SIZE)
+ return 0;
+
+ for (j = 0; j < i; j++) {
+ pci_free_consistent(
+ priv->pci_dev,
+ sizeof(struct ipw2100_cmd_header),
+ priv->msg_buffers[j].info.c_struct.cmd,
+ priv->msg_buffers[j].info.c_struct.cmd_phys);
+ }
+
+ kfree(priv->msg_buffers);
+ priv->msg_buffers = NULL;
+
+ return err;
+}
+
+static int ipw2100_msg_initialize(struct ipw2100_priv *priv)
+{
+ int i;
+
+ INIT_LIST_HEAD(&priv->msg_free_list);
+ INIT_LIST_HEAD(&priv->msg_pend_list);
+
+ for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++)
+ list_add_tail(&priv->msg_buffers[i].list, &priv->msg_free_list);
+ SET_STAT(&priv->msg_free_stat, i);
+
+ return 0;
+}
+
+static void ipw2100_msg_free(struct ipw2100_priv *priv)
+{
+ int i;
+
+ if (!priv->msg_buffers)
+ return;
+
+ for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
+ pci_free_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_cmd_header),
+ priv->msg_buffers[i].info.c_struct.cmd,
+ priv->msg_buffers[i].info.c_struct.cmd_phys);
+ }
+
+ kfree(priv->msg_buffers);
+ priv->msg_buffers = NULL;
+}
+
+static ssize_t show_pci(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pci_dev = container_of(d, struct pci_dev, dev);
+ char *out = buf;
+ int i, j;
+ u32 val;
+
+ for (i = 0; i < 16; i++) {
+ out += sprintf(out, "[%08X] ", i * 16);
+ for (j = 0; j < 16; j += 4) {
+ pci_read_config_dword(pci_dev, i * 16 + j, &val);
+ out += sprintf(out, "%08X ", val);
+ }
+ out += sprintf(out, "\n");
+ }
+
+ return out - buf;
+}
+static DEVICE_ATTR(pci, S_IRUGO, show_pci, NULL);
+
+static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *p = d->driver_data;
+ return sprintf(buf, "0x%08x\n", (int)p->config);
+}
+static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
+
+static ssize_t show_status(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *p = d->driver_data;
+ return sprintf(buf, "0x%08x\n", (int)p->status);
+}
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+
+static ssize_t show_capability(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *p = d->driver_data;
+ return sprintf(buf, "0x%08x\n", (int)p->capability);
+}
+static DEVICE_ATTR(capability, S_IRUGO, show_capability, NULL);
+
+
+#define IPW2100_REG(x) { IPW_ ##x, #x }
+static const struct {
+ u32 addr;
+ const char *name;
+} hw_data[] = {
+ IPW2100_REG(REG_GP_CNTRL),
+ IPW2100_REG(REG_GPIO),
+ IPW2100_REG(REG_INTA),
+ IPW2100_REG(REG_INTA_MASK),
+ IPW2100_REG(REG_RESET_REG),
+};
+#define IPW2100_NIC(x, s) { x, #x, s }
+static const struct {
+ u32 addr;
+ const char *name;
+ size_t size;
+} nic_data[] = {
+ IPW2100_NIC(IPW2100_CONTROL_REG, 2),
+ IPW2100_NIC(0x210014, 1),
+ IPW2100_NIC(0x210000, 1),
+};
+#define IPW2100_ORD(x, d) { IPW_ORD_ ##x, #x, d }
+static const struct {
+ u8 index;
+ const char *name;
+ const char *desc;
+} ord_data[] = {
+ IPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"),
+ IPW2100_ORD(STAT_TX_HOST_COMPLETE, "successful Host Tx's (MSDU)"),
+ IPW2100_ORD(STAT_TX_DIR_DATA, "successful Directed Tx's (MSDU)"),
+ IPW2100_ORD(STAT_TX_DIR_DATA1, "successful Directed Tx's (MSDU) @ 1MB"),
+ IPW2100_ORD(STAT_TX_DIR_DATA2, "successful Directed Tx's (MSDU) @ 2MB"),
+ IPW2100_ORD(STAT_TX_DIR_DATA5_5, "successful Directed Tx's (MSDU) @ 5_5MB"),
+ IPW2100_ORD(STAT_TX_DIR_DATA11, "successful Directed Tx's (MSDU) @ 11MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA1, "successful Non_Directed Tx's (MSDU) @ 1MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA2, "successful Non_Directed Tx's (MSDU) @ 2MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA5_5, "successful Non_Directed Tx's (MSDU) @ 5.5MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA11, "successful Non_Directed Tx's (MSDU) @ 11MB"),
+ IPW2100_ORD(STAT_NULL_DATA, "successful NULL data Tx's"),
+ IPW2100_ORD(STAT_TX_RTS, "successful Tx RTS"),
+ IPW2100_ORD(STAT_TX_CTS, "successful Tx CTS"),
+ IPW2100_ORD(STAT_TX_ACK, "successful Tx ACK"),
+ IPW2100_ORD(STAT_TX_ASSN, "successful Association Tx's"),
+ IPW2100_ORD(STAT_TX_ASSN_RESP, "successful Association response Tx's"),
+ IPW2100_ORD(STAT_TX_REASSN, "successful Reassociation Tx's"),
+ IPW2100_ORD(STAT_TX_REASSN_RESP, "successful Reassociation response Tx's"),
+ IPW2100_ORD(STAT_TX_PROBE, "probes successfully transmitted"),
+ IPW2100_ORD(STAT_TX_PROBE_RESP, "probe responses successfully transmitted"),
+ IPW2100_ORD(STAT_TX_BEACON, "tx beacon"),
+ IPW2100_ORD(STAT_TX_ATIM, "Tx ATIM"),
+ IPW2100_ORD(STAT_TX_DISASSN, "successful Disassociation TX"),
+ IPW2100_ORD(STAT_TX_AUTH, "successful Authentication Tx"),
+ IPW2100_ORD(STAT_TX_DEAUTH, "successful Deauthentication TX"),
+ IPW2100_ORD(STAT_TX_TOTAL_BYTES, "Total successful Tx data bytes"),
+ IPW2100_ORD(STAT_TX_RETRIES, "Tx retries"),
+ IPW2100_ORD(STAT_TX_RETRY1, "Tx retries at 1MBPS"),
+ IPW2100_ORD(STAT_TX_RETRY2, "Tx retries at 2MBPS"),
+ IPW2100_ORD(STAT_TX_RETRY5_5, "Tx retries at 5.5MBPS"),
+ IPW2100_ORD(STAT_TX_RETRY11, "Tx retries at 11MBPS"),
+ IPW2100_ORD(STAT_TX_FAILURES, "Tx Failures"),
+ IPW2100_ORD(STAT_TX_MAX_TRIES_IN_HOP,"times max tries in a hop failed"),
+ IPW2100_ORD(STAT_TX_DISASSN_FAIL, "times disassociation failed"),
+ IPW2100_ORD(STAT_TX_ERR_CTS, "missed/bad CTS frames"),
+ IPW2100_ORD(STAT_TX_ERR_ACK, "tx err due to acks"),
+ IPW2100_ORD(STAT_RX_HOST, "packets passed to host"),
+ IPW2100_ORD(STAT_RX_DIR_DATA, "directed packets"),
+ IPW2100_ORD(STAT_RX_DIR_DATA1, "directed packets at 1MB"),
+ IPW2100_ORD(STAT_RX_DIR_DATA2, "directed packets at 2MB"),
+ IPW2100_ORD(STAT_RX_DIR_DATA5_5, "directed packets at 5.5MB"),
+ IPW2100_ORD(STAT_RX_DIR_DATA11, "directed packets at 11MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA,"nondirected packets"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA1, "nondirected packets at 1MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA2, "nondirected packets at 2MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA5_5, "nondirected packets at 5.5MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA11, "nondirected packets at 11MB"),
+ IPW2100_ORD(STAT_RX_NULL_DATA, "null data rx's"),
+ IPW2100_ORD(STAT_RX_RTS, "Rx RTS"),
+ IPW2100_ORD(STAT_RX_CTS, "Rx CTS"),
+ IPW2100_ORD(STAT_RX_ACK, "Rx ACK"),
+ IPW2100_ORD(STAT_RX_CFEND, "Rx CF End"),
+ IPW2100_ORD(STAT_RX_CFEND_ACK, "Rx CF End + CF Ack"),
+ IPW2100_ORD(STAT_RX_ASSN, "Association Rx's"),
+ IPW2100_ORD(STAT_RX_ASSN_RESP, "Association response Rx's"),
+ IPW2100_ORD(STAT_RX_REASSN, "Reassociation Rx's"),
+ IPW2100_ORD(STAT_RX_REASSN_RESP, "Reassociation response Rx's"),
+ IPW2100_ORD(STAT_RX_PROBE, "probe Rx's"),
+ IPW2100_ORD(STAT_RX_PROBE_RESP, "probe response Rx's"),
+ IPW2100_ORD(STAT_RX_BEACON, "Rx beacon"),
+ IPW2100_ORD(STAT_RX_ATIM, "Rx ATIM"),
+ IPW2100_ORD(STAT_RX_DISASSN, "disassociation Rx"),
+ IPW2100_ORD(STAT_RX_AUTH, "authentication Rx"),
+ IPW2100_ORD(STAT_RX_DEAUTH, "deauthentication Rx"),
+ IPW2100_ORD(STAT_RX_TOTAL_BYTES,"Total rx data bytes received"),
+ IPW2100_ORD(STAT_RX_ERR_CRC, "packets with Rx CRC error"),
+ IPW2100_ORD(STAT_RX_ERR_CRC1, "Rx CRC errors at 1MB"),
+ IPW2100_ORD(STAT_RX_ERR_CRC2, "Rx CRC errors at 2MB"),
+ IPW2100_ORD(STAT_RX_ERR_CRC5_5, "Rx CRC errors at 5.5MB"),
+ IPW2100_ORD(STAT_RX_ERR_CRC11, "Rx CRC errors at 11MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE1, "duplicate rx packets at 1MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE2, "duplicate rx packets at 2MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE5_5, "duplicate rx packets at 5.5MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE11, "duplicate rx packets at 11MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE, "duplicate rx packets"),
+ IPW2100_ORD(PERS_DB_LOCK, "locking fw permanent db"),
+ IPW2100_ORD(PERS_DB_SIZE, "size of fw permanent db"),
+ IPW2100_ORD(PERS_DB_ADDR, "address of fw permanent db"),
+ IPW2100_ORD(STAT_RX_INVALID_PROTOCOL, "rx frames with invalid protocol"),
+ IPW2100_ORD(SYS_BOOT_TIME, "Boot time"),
+ IPW2100_ORD(STAT_RX_NO_BUFFER, "rx frames rejected due to no buffer"),
+ IPW2100_ORD(STAT_RX_MISSING_FRAG, "rx frames dropped due to missing fragment"),
+ IPW2100_ORD(STAT_RX_ORPHAN_FRAG, "rx frames dropped due to non-sequential fragment"),
+ IPW2100_ORD(STAT_RX_ORPHAN_FRAME, "rx frames dropped due to unmatched 1st frame"),
+ IPW2100_ORD(STAT_RX_FRAG_AGEOUT, "rx frames dropped due to uncompleted frame"),
+ IPW2100_ORD(STAT_RX_ICV_ERRORS, "ICV errors during decryption"),
+ IPW2100_ORD(STAT_PSP_SUSPENSION,"times adapter suspended"),
+ IPW2100_ORD(STAT_PSP_BCN_TIMEOUT, "beacon timeout"),
+ IPW2100_ORD(STAT_PSP_POLL_TIMEOUT, "poll response timeouts"),
+ IPW2100_ORD(STAT_PSP_NONDIR_TIMEOUT, "timeouts waiting for last {broad,multi}cast pkt"),
+ IPW2100_ORD(STAT_PSP_RX_DTIMS, "PSP DTIMs received"),
+ IPW2100_ORD(STAT_PSP_RX_TIMS, "PSP TIMs received"),
+ IPW2100_ORD(STAT_PSP_STATION_ID,"PSP Station ID"),
+ IPW2100_ORD(LAST_ASSN_TIME, "RTC time of last association"),
+ IPW2100_ORD(STAT_PERCENT_MISSED_BCNS,"current calculation of % missed beacons"),
+ IPW2100_ORD(STAT_PERCENT_RETRIES,"current calculation of % missed tx retries"),
+ IPW2100_ORD(ASSOCIATED_AP_PTR, "0 if not associated, else pointer to AP table entry"),
+ IPW2100_ORD(AVAILABLE_AP_CNT, "AP's decsribed in the AP table"),
+ IPW2100_ORD(AP_LIST_PTR, "Ptr to list of available APs"),
+ IPW2100_ORD(STAT_AP_ASSNS, "associations"),
+ IPW2100_ORD(STAT_ASSN_FAIL, "association failures"),
+ IPW2100_ORD(STAT_ASSN_RESP_FAIL,"failures due to response fail"),
+ IPW2100_ORD(STAT_FULL_SCANS, "full scans"),
+ IPW2100_ORD(CARD_DISABLED, "Card Disabled"),
+ IPW2100_ORD(STAT_ROAM_INHIBIT, "times roaming was inhibited due to activity"),
+ IPW2100_ORD(RSSI_AT_ASSN, "RSSI of associated AP at time of association"),
+ IPW2100_ORD(STAT_ASSN_CAUSE1, "reassociation: no probe response or TX on hop"),
+ IPW2100_ORD(STAT_ASSN_CAUSE2, "reassociation: poor tx/rx quality"),
+ IPW2100_ORD(STAT_ASSN_CAUSE3, "reassociation: tx/rx quality (excessive AP load"),
+ IPW2100_ORD(STAT_ASSN_CAUSE4, "reassociation: AP RSSI level"),
+ IPW2100_ORD(STAT_ASSN_CAUSE5, "reassociations due to load leveling"),
+ IPW2100_ORD(STAT_AUTH_FAIL, "times authentication failed"),
+ IPW2100_ORD(STAT_AUTH_RESP_FAIL,"times authentication response failed"),
+ IPW2100_ORD(STATION_TABLE_CNT, "entries in association table"),
+ IPW2100_ORD(RSSI_AVG_CURR, "Current avg RSSI"),
+ IPW2100_ORD(POWER_MGMT_MODE, "Power mode - 0=CAM, 1=PSP"),
+ IPW2100_ORD(COUNTRY_CODE, "IEEE country code as recv'd from beacon"),
+ IPW2100_ORD(COUNTRY_CHANNELS, "channels suported by country"),
+ IPW2100_ORD(RESET_CNT, "adapter resets (warm)"),
+ IPW2100_ORD(BEACON_INTERVAL, "Beacon interval"),
+ IPW2100_ORD(ANTENNA_DIVERSITY, "TRUE if antenna diversity is disabled"),
+ IPW2100_ORD(DTIM_PERIOD, "beacon intervals between DTIMs"),
+ IPW2100_ORD(OUR_FREQ, "current radio freq lower digits - channel ID"),
+ IPW2100_ORD(RTC_TIME, "current RTC time"),
+ IPW2100_ORD(PORT_TYPE, "operating mode"),
+ IPW2100_ORD(CURRENT_TX_RATE, "current tx rate"),
+ IPW2100_ORD(SUPPORTED_RATES, "supported tx rates"),
+ IPW2100_ORD(ATIM_WINDOW, "current ATIM Window"),
+ IPW2100_ORD(BASIC_RATES, "basic tx rates"),
+ IPW2100_ORD(NIC_HIGHEST_RATE, "NIC highest tx rate"),
+ IPW2100_ORD(AP_HIGHEST_RATE, "AP highest tx rate"),
+ IPW2100_ORD(CAPABILITIES, "Management frame capability field"),
+ IPW2100_ORD(AUTH_TYPE, "Type of authentication"),
+ IPW2100_ORD(RADIO_TYPE, "Adapter card platform type"),
+ IPW2100_ORD(RTS_THRESHOLD, "Min packet length for RTS handshaking"),
+ IPW2100_ORD(INT_MODE, "International mode"),
+ IPW2100_ORD(FRAGMENTATION_THRESHOLD, "protocol frag threshold"),
+ IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_START_ADDRESS, "EEPROM offset in SRAM"),
+ IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_SIZE, "EEPROM size in SRAM"),
+ IPW2100_ORD(EEPROM_SKU_CAPABILITY, "EEPROM SKU Capability"),
+ IPW2100_ORD(EEPROM_IBSS_11B_CHANNELS, "EEPROM IBSS 11b channel set"),
+ IPW2100_ORD(MAC_VERSION, "MAC Version"),
+ IPW2100_ORD(MAC_REVISION, "MAC Revision"),
+ IPW2100_ORD(RADIO_VERSION, "Radio Version"),
+ IPW2100_ORD(NIC_MANF_DATE_TIME, "MANF Date/Time STAMP"),
+ IPW2100_ORD(UCODE_VERSION, "Ucode Version"),
+};
+
+
+static ssize_t show_registers(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ int i;
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ char * out = buf;
+ u32 val = 0;
+
+ out += sprintf(out, "%30s [Address ] : Hex\n", "Register");
+
+ for (i = 0; i < (sizeof(hw_data) / sizeof(*hw_data)); i++) {
+ read_register(dev, hw_data[i].addr, &val);
+ out += sprintf(out, "%30s [%08X] : %08X\n",
+ hw_data[i].name, hw_data[i].addr, val);
+ }
+
+ return out - buf;
+}
+static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
+
+
+static ssize_t show_hardware(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ char * out = buf;
+ int i;
+
+ out += sprintf(out, "%30s [Address ] : Hex\n", "NIC entry");
+
+ for (i = 0; i < (sizeof(nic_data) / sizeof(*nic_data)); i++) {
+ u8 tmp8;
+ u16 tmp16;
+ u32 tmp32;
+
+ switch (nic_data[i].size) {
+ case 1:
+ read_nic_byte(dev, nic_data[i].addr, &tmp8);
+ out += sprintf(out, "%30s [%08X] : %02X\n",
+ nic_data[i].name, nic_data[i].addr,
+ tmp8);
+ break;
+ case 2:
+ read_nic_word(dev, nic_data[i].addr, &tmp16);
+ out += sprintf(out, "%30s [%08X] : %04X\n",
+ nic_data[i].name, nic_data[i].addr,
+ tmp16);
+ break;
+ case 4:
+ read_nic_dword(dev, nic_data[i].addr, &tmp32);
+ out += sprintf(out, "%30s [%08X] : %08X\n",
+ nic_data[i].name, nic_data[i].addr,
+ tmp32);
+ break;
+ }
+ }
+ return out - buf;
+}
+static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL);
+
+
+static ssize_t show_memory(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ static unsigned long loop = 0;
+ int len = 0;
+ u32 buffer[4];
+ int i;
+ char line[81];
+
+ if (loop >= 0x30000)
+ loop = 0;
+
+ /* sysfs provides us PAGE_SIZE buffer */
+ while (len < PAGE_SIZE - 128 && loop < 0x30000) {
+
+ if (priv->snapshot[0]) for (i = 0; i < 4; i++)
+ buffer[i] = *(u32 *)SNAPSHOT_ADDR(loop + i * 4);
+ else for (i = 0; i < 4; i++)
+ read_nic_dword(dev, loop + i * 4, &buffer[i]);
+
+ if (priv->dump_raw)
+ len += sprintf(buf + len,
+ "%c%c%c%c"
+ "%c%c%c%c"
+ "%c%c%c%c"
+ "%c%c%c%c",
+ ((u8*)buffer)[0x0],
+ ((u8*)buffer)[0x1],
+ ((u8*)buffer)[0x2],
+ ((u8*)buffer)[0x3],
+ ((u8*)buffer)[0x4],
+ ((u8*)buffer)[0x5],
+ ((u8*)buffer)[0x6],
+ ((u8*)buffer)[0x7],
+ ((u8*)buffer)[0x8],
+ ((u8*)buffer)[0x9],
+ ((u8*)buffer)[0xa],
+ ((u8*)buffer)[0xb],
+ ((u8*)buffer)[0xc],
+ ((u8*)buffer)[0xd],
+ ((u8*)buffer)[0xe],
+ ((u8*)buffer)[0xf]);
+ else
+ len += sprintf(buf + len, "%s\n",
+ snprint_line(line, sizeof(line),
+ (u8*)buffer, 16, loop));
+ loop += 16;
+ }
+
+ return len;
+}
+
+static ssize_t store_memory(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ const char *p = buf;
+
+ if (count < 1)
+ return count;
+
+ if (p[0] == '1' ||
+ (count >= 2 && tolower(p[0]) == 'o' && tolower(p[1]) == 'n')) {
+ IPW_DEBUG_INFO("%s: Setting memory dump to RAW mode.\n",
+ dev->name);
+ priv->dump_raw = 1;
+
+ } else if (p[0] == '0' || (count >= 2 && tolower(p[0]) == 'o' &&
+ tolower(p[1]) == 'f')) {
+ IPW_DEBUG_INFO("%s: Setting memory dump to HEX mode.\n",
+ dev->name);
+ priv->dump_raw = 0;
+
+ } else if (tolower(p[0]) == 'r') {
+ IPW_DEBUG_INFO("%s: Resetting firmware snapshot.\n",
+ dev->name);
+ ipw2100_snapshot_free(priv);
+
+ } else
+ IPW_DEBUG_INFO("%s: Usage: 0|on = HEX, 1|off = RAW, "
+ "reset = clear memory snapshot\n",
+ dev->name);
+
+ return count;
+}
+static DEVICE_ATTR(memory, S_IWUSR|S_IRUGO, show_memory, store_memory);
+
+
+static ssize_t show_ordinals(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ u32 val = 0;
+ int len = 0;
+ u32 val_len;
+ static int loop = 0;
+
+ if (loop >= sizeof(ord_data) / sizeof(*ord_data))
+ loop = 0;
+
+ /* sysfs provides us PAGE_SIZE buffer */
+ while (len < PAGE_SIZE - 128 &&
+ loop < (sizeof(ord_data) / sizeof(*ord_data))) {
+
+ val_len = sizeof(u32);
+
+ if (ipw2100_get_ordinal(priv, ord_data[loop].index, &val,
+ &val_len))
+ len += sprintf(buf + len, "[0x%02X] = ERROR %s\n",
+ ord_data[loop].index,
+ ord_data[loop].desc);
+ else
+ len += sprintf(buf + len, "[0x%02X] = 0x%08X %s\n",
+ ord_data[loop].index, val,
+ ord_data[loop].desc);
+ loop++;
+ }
+
+ return len;
+}
+static DEVICE_ATTR(ordinals, S_IRUGO, show_ordinals, NULL);
+
+
+static ssize_t show_stats(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ char * out = buf;
+
+ out += sprintf(out, "interrupts: %d {tx: %d, rx: %d, other: %d}\n",
+ priv->interrupts, priv->tx_interrupts,
+ priv->rx_interrupts, priv->inta_other);
+ out += sprintf(out, "firmware resets: %d\n", priv->resets);
+ out += sprintf(out, "firmware hangs: %d\n", priv->hangs);
+#ifdef CONFIG_IPW_DEBUG
+ out += sprintf(out, "packet mismatch image: %s\n",
+ priv->snapshot[0] ? "YES" : "NO");
+#endif
+
+ return out - buf;
+}
+static DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL);
+
+
+static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode)
+{
+ int err;
+
+ if (mode == priv->ieee->iw_mode)
+ return 0;
+
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+
+ switch (mode) {
+ case IW_MODE_INFRA:
+ priv->net_dev->type = ARPHRD_ETHER;
+ break;
+ case IW_MODE_ADHOC:
+ priv->net_dev->type = ARPHRD_ETHER;
+ break;
+#ifdef CONFIG_IPW2100_MONITOR
+ case IW_MODE_MONITOR:
+ priv->last_mode = priv->ieee->iw_mode;
+ priv->net_dev->type = ARPHRD_IEEE80211;
+ break;
+#endif /* CONFIG_IPW2100_MONITOR */
+ }
+
+ priv->ieee->iw_mode = mode;
+
+#ifdef CONFIG_PM
+ /* Indicate ipw2100_download_firmware download firmware
+ * from disk instead of memory. */
+ ipw2100_firmware.version = 0;
+#endif
+
+ printk(KERN_INFO "%s: Reseting on mode change.\n",
+ priv->net_dev->name);
+ priv->reset_backoff = 0;
+ schedule_reset(priv);
+
+ return 0;
+}
+
+static ssize_t show_internals(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ int len = 0;
+
+#define DUMP_VAR(x,y) len += sprintf(buf + len, # x ": %" # y "\n", priv-> x)
+
+ if (priv->status & STATUS_ASSOCIATED)
+ len += sprintf(buf + len, "connected: %lu\n",
+ get_seconds() - priv->connect_start);
+ else
+ len += sprintf(buf + len, "not connected\n");
+
+ DUMP_VAR(ieee->crypt[priv->ieee->tx_keyidx], p);
+ DUMP_VAR(status, 08lx);
+ DUMP_VAR(config, 08lx);
+ DUMP_VAR(capability, 08lx);
+
+ len += sprintf(buf + len, "last_rtc: %lu\n", (unsigned long)priv->last_rtc);
+
+ DUMP_VAR(fatal_error, d);
+ DUMP_VAR(stop_hang_check, d);
+ DUMP_VAR(stop_rf_kill, d);
+ DUMP_VAR(messages_sent, d);
+
+ DUMP_VAR(tx_pend_stat.value, d);
+ DUMP_VAR(tx_pend_stat.hi, d);
+
+ DUMP_VAR(tx_free_stat.value, d);
+ DUMP_VAR(tx_free_stat.lo, d);
+
+ DUMP_VAR(msg_free_stat.value, d);
+ DUMP_VAR(msg_free_stat.lo, d);
+
+ DUMP_VAR(msg_pend_stat.value, d);
+ DUMP_VAR(msg_pend_stat.hi, d);
+
+ DUMP_VAR(fw_pend_stat.value, d);
+ DUMP_VAR(fw_pend_stat.hi, d);
+
+ DUMP_VAR(txq_stat.value, d);
+ DUMP_VAR(txq_stat.lo, d);
+
+ DUMP_VAR(ieee->scans, d);
+ DUMP_VAR(reset_backoff, d);
+
+ return len;
+}
+static DEVICE_ATTR(internals, S_IRUGO, show_internals, NULL);
+
+
+static ssize_t show_bssinfo(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ char essid[IW_ESSID_MAX_SIZE + 1];
+ u8 bssid[ETH_ALEN];
+ u32 chan = 0;
+ char * out = buf;
+ int length;
+ int ret;
+
+ memset(essid, 0, sizeof(essid));
+ memset(bssid, 0, sizeof(bssid));
+
+ length = IW_ESSID_MAX_SIZE;
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, essid, &length);
+ if (ret)
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+
+ length = sizeof(bssid);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
+ bssid, &length);
+ if (ret)
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+
+ length = sizeof(u32);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &length);
+ if (ret)
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+
+ out += sprintf(out, "ESSID: %s\n", essid);
+ out += sprintf(out, "BSSID: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ bssid[0], bssid[1], bssid[2],
+ bssid[3], bssid[4], bssid[5]);
+ out += sprintf(out, "Channel: %d\n", chan);
+
+ return out - buf;
+}
+static DEVICE_ATTR(bssinfo, S_IRUGO, show_bssinfo, NULL);
+
+
+#ifdef CONFIG_IPW_DEBUG
+static ssize_t show_debug_level(struct device_driver *d, char *buf)
+{
+ return sprintf(buf, "0x%08X\n", ipw2100_debug_level);
+}
+
+static ssize_t store_debug_level(struct device_driver *d, const char *buf,
+ size_t count)
+{
+ char *p = (char *)buf;
+ u32 val;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ p++;
+ if (p[0] == 'x' || p[0] == 'X')
+ p++;
+ val = simple_strtoul(p, &p, 16);
+ } else
+ val = simple_strtoul(p, &p, 10);
+ if (p == buf)
+ IPW_DEBUG_INFO(DRV_NAME
+ ": %s is not in hex or decimal form.\n", buf);
+ else
+ ipw2100_debug_level = val;
+
+ return strnlen(buf, count);
+}
+static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, show_debug_level,
+ store_debug_level);
+#endif /* CONFIG_IPW_DEBUG */
+
+
+static ssize_t show_fatal_error(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ char *out = buf;
+ int i;
+
+ if (priv->fatal_error)
+ out += sprintf(out, "0x%08X\n",
+ priv->fatal_error);
+ else
+ out += sprintf(out, "0\n");
+
+ for (i = 1; i <= IPW2100_ERROR_QUEUE; i++) {
+ if (!priv->fatal_errors[(priv->fatal_index - i) %
+ IPW2100_ERROR_QUEUE])
+ continue;
+
+ out += sprintf(out, "%d. 0x%08X\n", i,
+ priv->fatal_errors[(priv->fatal_index - i) %
+ IPW2100_ERROR_QUEUE]);
+ }
+
+ return out - buf;
+}
+
+static ssize_t store_fatal_error(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ schedule_reset(priv);
+ return count;
+}
+static DEVICE_ATTR(fatal_error, S_IWUSR|S_IRUGO, show_fatal_error, store_fatal_error);
+
+
+static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ return sprintf(buf, "%d\n", priv->ieee->scan_age);
+}
+
+static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ char buffer[] = "00000000";
+ unsigned long len =
+ (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
+ unsigned long val;
+ char *p = buffer;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ strncpy(buffer, buf, len);
+ buffer[len] = 0;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ p++;
+ if (p[0] == 'x' || p[0] == 'X')
+ p++;
+ val = simple_strtoul(p, &p, 16);
+ } else
+ val = simple_strtoul(p, &p, 10);
+ if (p == buffer) {
+ IPW_DEBUG_INFO("%s: user supplied invalid value.\n",
+ dev->name);
+ } else {
+ priv->ieee->scan_age = val;
+ IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+ return len;
+}
+static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
+
+
+static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ /* 0 - RF kill not enabled
+ 1 - SW based RF kill active (sysfs)
+ 2 - HW based RF kill active
+ 3 - Both HW and SW baed RF kill active */
+ struct ipw2100_priv *priv = (struct ipw2100_priv *)d->driver_data;
+ int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
+ (rf_kill_active(priv) ? 0x2 : 0x0);
+ return sprintf(buf, "%i\n", val);
+}
+
+static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio)
+{
+ if ((disable_radio ? 1 : 0) ==
+ (priv->status & STATUS_RF_KILL_SW ? 1 : 0))
+ return 0 ;
+
+ IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
+ disable_radio ? "OFF" : "ON");
+
+ down(&priv->action_sem);
+
+ if (disable_radio) {
+ priv->status |= STATUS_RF_KILL_SW;
+ ipw2100_down(priv);
+ } else {
+ priv->status &= ~STATUS_RF_KILL_SW;
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_RF_KILL("Can not turn radio back on - "
+ "disabled by HW switch\n");
+ /* Make sure the RF_KILL check timer is running */
+ priv->stop_rf_kill = 0;
+ cancel_delayed_work(&priv->rf_kill);
+ queue_delayed_work(priv->workqueue, &priv->rf_kill,
+ HZ);
+ } else
+ schedule_reset(priv);
+ }
+
+ up(&priv->action_sem);
+ return 1;
+}
+
+static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ ipw_radio_kill_sw(priv, buf[0] == '1');
+ return count;
+}
+static DEVICE_ATTR(rf_kill, S_IWUSR|S_IRUGO, show_rf_kill, store_rf_kill);
+
+
+static struct attribute *ipw2100_sysfs_entries[] = {
+ &dev_attr_hardware.attr,
+ &dev_attr_registers.attr,
+ &dev_attr_ordinals.attr,
+ &dev_attr_pci.attr,
+ &dev_attr_stats.attr,
+ &dev_attr_internals.attr,
+ &dev_attr_bssinfo.attr,
+ &dev_attr_memory.attr,
+ &dev_attr_scan_age.attr,
+ &dev_attr_fatal_error.attr,
+ &dev_attr_rf_kill.attr,
+ &dev_attr_cfg.attr,
+ &dev_attr_status.attr,
+ &dev_attr_capability.attr,
+ NULL,
+};
+
+static struct attribute_group ipw2100_attribute_group = {
+ .attrs = ipw2100_sysfs_entries,
+};
+
+
+static int status_queue_allocate(struct ipw2100_priv *priv, int entries)
+{
+ struct ipw2100_status_queue *q = &priv->status_queue;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ q->size = entries * sizeof(struct ipw2100_status);
+ q->drv = (struct ipw2100_status *)pci_alloc_consistent(
+ priv->pci_dev, q->size, &q->nic);
+ if (!q->drv) {
+ IPW_DEBUG_WARNING(
+ "Can not allocate status queue.\n");
+ return -ENOMEM;
+ }
+
+ memset(q->drv, 0, q->size);
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+static void status_queue_free(struct ipw2100_priv *priv)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->status_queue.drv) {
+ pci_free_consistent(
+ priv->pci_dev, priv->status_queue.size,
+ priv->status_queue.drv, priv->status_queue.nic);
+ priv->status_queue.drv = NULL;
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static int bd_queue_allocate(struct ipw2100_priv *priv,
+ struct ipw2100_bd_queue *q, int entries)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ memset(q, 0, sizeof(struct ipw2100_bd_queue));
+
+ q->entries = entries;
+ q->size = entries * sizeof(struct ipw2100_bd);
+ q->drv = pci_alloc_consistent(priv->pci_dev, q->size, &q->nic);
+ if (!q->drv) {
+ IPW_DEBUG_INFO("can't allocate shared memory for buffer descriptors\n");
+ return -ENOMEM;
+ }
+ memset(q->drv, 0, q->size);
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+static void bd_queue_free(struct ipw2100_priv *priv,
+ struct ipw2100_bd_queue *q)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ if (!q)
+ return;
+
+ if (q->drv) {
+ pci_free_consistent(priv->pci_dev,
+ q->size, q->drv, q->nic);
+ q->drv = NULL;
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static void bd_queue_initialize(
+ struct ipw2100_priv *priv, struct ipw2100_bd_queue * q,
+ u32 base, u32 size, u32 r, u32 w)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ IPW_DEBUG_INFO("initializing bd queue at virt=%p, phys=%08x\n", q->drv, (u32)q->nic);
+
+ write_register(priv->net_dev, base, q->nic);
+ write_register(priv->net_dev, size, q->entries);
+ write_register(priv->net_dev, r, q->oldest);
+ write_register(priv->net_dev, w, q->next);
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static void ipw2100_kill_workqueue(struct ipw2100_priv *priv)
+{
+ if (priv->workqueue) {
+ priv->stop_rf_kill = 1;
+ priv->stop_hang_check = 1;
+ cancel_delayed_work(&priv->reset_work);
+ cancel_delayed_work(&priv->security_work);
+ cancel_delayed_work(&priv->wx_event_work);
+ cancel_delayed_work(&priv->hang_check);
+ cancel_delayed_work(&priv->rf_kill);
+ destroy_workqueue(priv->workqueue);
+ priv->workqueue = NULL;
+ }
+}
+
+static int ipw2100_tx_allocate(struct ipw2100_priv *priv)
+{
+ int i, j, err = -EINVAL;
+ void *v;
+ dma_addr_t p;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ err = bd_queue_allocate(priv, &priv->tx_queue, TX_QUEUE_LENGTH);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: failed bd_queue_allocate\n",
+ priv->net_dev->name);
+ return err;
+ }
+
+ priv->tx_buffers = (struct ipw2100_tx_packet *)kmalloc(
+ TX_PENDED_QUEUE_LENGTH * sizeof(struct ipw2100_tx_packet),
+ GFP_ATOMIC);
+ if (!priv->tx_buffers) {
+ printk(KERN_ERR DRV_NAME ": %s: alloc failed form tx buffers.\n",
+ priv->net_dev->name);
+ bd_queue_free(priv, &priv->tx_queue);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+ v = pci_alloc_consistent(
+ priv->pci_dev, sizeof(struct ipw2100_data_header), &p);
+ if (!v) {
+ printk(KERN_ERR DRV_NAME ": %s: PCI alloc failed for tx "
+ "buffers.\n", priv->net_dev->name);
+ err = -ENOMEM;
+ break;
+ }
+
+ priv->tx_buffers[i].type = DATA;
+ priv->tx_buffers[i].info.d_struct.data = (struct ipw2100_data_header*)v;
+ priv->tx_buffers[i].info.d_struct.data_phys = p;
+ priv->tx_buffers[i].info.d_struct.txb = NULL;
+ }
+
+ if (i == TX_PENDED_QUEUE_LENGTH)
+ return 0;
+
+ for (j = 0; j < i; j++) {
+ pci_free_consistent(
+ priv->pci_dev,
+ sizeof(struct ipw2100_data_header),
+ priv->tx_buffers[j].info.d_struct.data,
+ priv->tx_buffers[j].info.d_struct.data_phys);
+ }
+
+ kfree(priv->tx_buffers);
+ priv->tx_buffers = NULL;
+
+ return err;
+}
+
+static void ipw2100_tx_initialize(struct ipw2100_priv *priv)
+{
+ int i;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ /*
+ * reinitialize packet info lists
+ */
+ INIT_LIST_HEAD(&priv->fw_pend_list);
+ INIT_STAT(&priv->fw_pend_stat);
+
+ /*
+ * reinitialize lists
+ */
+ INIT_LIST_HEAD(&priv->tx_pend_list);
+ INIT_LIST_HEAD(&priv->tx_free_list);
+ INIT_STAT(&priv->tx_pend_stat);
+ INIT_STAT(&priv->tx_free_stat);
+
+ for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+ /* We simply drop any SKBs that have been queued for
+ * transmit */
+ if (priv->tx_buffers[i].info.d_struct.txb) {
+ ieee80211_txb_free(priv->tx_buffers[i].info.d_struct.txb);
+ priv->tx_buffers[i].info.d_struct.txb = NULL;
+ }
+
+ list_add_tail(&priv->tx_buffers[i].list, &priv->tx_free_list);
+ }
+
+ SET_STAT(&priv->tx_free_stat, i);
+
+ priv->tx_queue.oldest = 0;
+ priv->tx_queue.available = priv->tx_queue.entries;
+ priv->tx_queue.next = 0;
+ INIT_STAT(&priv->txq_stat);
+ SET_STAT(&priv->txq_stat, priv->tx_queue.available);
+
+ bd_queue_initialize(priv, &priv->tx_queue,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX);
+
+ IPW_DEBUG_INFO("exit\n");
+
+}
+
+static void ipw2100_tx_free(struct ipw2100_priv *priv)
+{
+ int i;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ bd_queue_free(priv, &priv->tx_queue);
+
+ if (!priv->tx_buffers)
+ return;
+
+ for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+ if (priv->tx_buffers[i].info.d_struct.txb) {
+ ieee80211_txb_free(priv->tx_buffers[i].info.d_struct.txb);
+ priv->tx_buffers[i].info.d_struct.txb = NULL;
+ }
+ if (priv->tx_buffers[i].info.d_struct.data)
+ pci_free_consistent(
+ priv->pci_dev,
+ sizeof(struct ipw2100_data_header),
+ priv->tx_buffers[i].info.d_struct.data,
+ priv->tx_buffers[i].info.d_struct.data_phys);
+ }
+
+ kfree(priv->tx_buffers);
+ priv->tx_buffers = NULL;
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+
+
+static int ipw2100_rx_allocate(struct ipw2100_priv *priv)
+{
+ int i, j, err = -EINVAL;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ err = bd_queue_allocate(priv, &priv->rx_queue, RX_QUEUE_LENGTH);
+ if (err) {
+ IPW_DEBUG_INFO("failed bd_queue_allocate\n");
+ return err;
+ }
+
+ err = status_queue_allocate(priv, RX_QUEUE_LENGTH);
+ if (err) {
+ IPW_DEBUG_INFO("failed status_queue_allocate\n");
+ bd_queue_free(priv, &priv->rx_queue);
+ return err;
+ }
+
+ /*
+ * allocate packets
+ */
+ priv->rx_buffers = (struct ipw2100_rx_packet *)
+ kmalloc(RX_QUEUE_LENGTH * sizeof(struct ipw2100_rx_packet),
+ GFP_KERNEL);
+ if (!priv->rx_buffers) {
+ IPW_DEBUG_INFO("can't allocate rx packet buffer table\n");
+
+ bd_queue_free(priv, &priv->rx_queue);
+
+ status_queue_free(priv);
+
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < RX_QUEUE_LENGTH; i++) {
+ struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+ err = ipw2100_alloc_skb(priv, packet);
+ if (unlikely(err)) {
+ err = -ENOMEM;
+ break;
+ }
+
+ /* The BD holds the cache aligned address */
+ priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+ priv->rx_queue.drv[i].buf_length = IPW_RX_NIC_BUFFER_LENGTH;
+ priv->status_queue.drv[i].status_fields = 0;
+ }
+
+ if (i == RX_QUEUE_LENGTH)
+ return 0;
+
+ for (j = 0; j < i; j++) {
+ pci_unmap_single(priv->pci_dev, priv->rx_buffers[j].dma_addr,
+ sizeof(struct ipw2100_rx_packet),
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(priv->rx_buffers[j].skb);
+ }
+
+ kfree(priv->rx_buffers);
+ priv->rx_buffers = NULL;
+
+ bd_queue_free(priv, &priv->rx_queue);
+
+ status_queue_free(priv);
+
+ return err;
+}
+
+static void ipw2100_rx_initialize(struct ipw2100_priv *priv)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ priv->rx_queue.oldest = 0;
+ priv->rx_queue.available = priv->rx_queue.entries - 1;
+ priv->rx_queue.next = priv->rx_queue.entries - 1;
+
+ INIT_STAT(&priv->rxq_stat);
+ SET_STAT(&priv->rxq_stat, priv->rx_queue.available);
+
+ bd_queue_initialize(priv, &priv->rx_queue,
+ IPW_MEM_HOST_SHARED_RX_BD_BASE,
+ IPW_MEM_HOST_SHARED_RX_BD_SIZE,
+ IPW_MEM_HOST_SHARED_RX_READ_INDEX,
+ IPW_MEM_HOST_SHARED_RX_WRITE_INDEX);
+
+ /* set up the status queue */
+ write_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_STATUS_BASE,
+ priv->status_queue.nic);
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static void ipw2100_rx_free(struct ipw2100_priv *priv)
+{
+ int i;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ bd_queue_free(priv, &priv->rx_queue);
+ status_queue_free(priv);
+
+ if (!priv->rx_buffers)
+ return;
+
+ for (i = 0; i < RX_QUEUE_LENGTH; i++) {
+ if (priv->rx_buffers[i].rxp) {
+ pci_unmap_single(priv->pci_dev,
+ priv->rx_buffers[i].dma_addr,
+ sizeof(struct ipw2100_rx),
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(priv->rx_buffers[i].skb);
+ }
+ }
+
+ kfree(priv->rx_buffers);
+ priv->rx_buffers = NULL;
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static int ipw2100_read_mac_address(struct ipw2100_priv *priv)
+{
+ u32 length = ETH_ALEN;
+ u8 mac[ETH_ALEN];
+
+ int err;
+
+ err = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ADAPTER_MAC,
+ mac, &length);
+ if (err) {
+ IPW_DEBUG_INFO("MAC address read failed\n");
+ return -EIO;
+ }
+ IPW_DEBUG_INFO("card MAC is %02X:%02X:%02X:%02X:%02X:%02X\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+ memcpy(priv->net_dev->dev_addr, mac, ETH_ALEN);
+
+ return 0;
+}
+
+/********************************************************************
+ *
+ * Firmware Commands
+ *
+ ********************************************************************/
+
+static int ipw2100_set_mac_address(struct ipw2100_priv *priv, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = ADAPTER_ADDRESS,
+ .host_command_sequence = 0,
+ .host_command_length = ETH_ALEN
+ };
+ int err;
+
+ IPW_DEBUG_HC("SET_MAC_ADDRESS\n");
+
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->config & CFG_CUSTOM_MAC) {
+ memcpy(cmd.host_command_parameters, priv->mac_addr,
+ ETH_ALEN);
+ memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
+ } else
+ memcpy(cmd.host_command_parameters, priv->net_dev->dev_addr,
+ ETH_ALEN);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ IPW_DEBUG_INFO("exit\n");
+ return err;
+}
+
+static int ipw2100_set_port_type(struct ipw2100_priv *priv, u32 port_type,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = PORT_TYPE,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(u32)
+ };
+ int err;
+
+ switch (port_type) {
+ case IW_MODE_INFRA:
+ cmd.host_command_parameters[0] = IPW_BSS;
+ break;
+ case IW_MODE_ADHOC:
+ cmd.host_command_parameters[0] = IPW_IBSS;
+ break;
+ }
+
+ IPW_DEBUG_HC("PORT_TYPE: %s\n",
+ port_type == IPW_IBSS ? "Ad-Hoc" : "Managed");
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+
+static int ipw2100_set_channel(struct ipw2100_priv *priv, u32 channel,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = CHANNEL,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(u32)
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = channel;
+
+ IPW_DEBUG_HC("CHANNEL: %d\n", channel);
+
+ /* If BSS then we don't support channel selection */
+ if (priv->ieee->iw_mode == IW_MODE_INFRA)
+ return 0;
+
+ if ((channel != 0) &&
+ ((channel < REG_MIN_CHANNEL) || (channel > REG_MAX_CHANNEL)))
+ return -EINVAL;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err) {
+ IPW_DEBUG_INFO("Failed to set channel to %d",
+ channel);
+ return err;
+ }
+
+ if (channel)
+ priv->config |= CFG_STATIC_CHANNEL;
+ else
+ priv->config &= ~CFG_STATIC_CHANNEL;
+
+ priv->channel = channel;
+
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int ipw2100_system_config(struct ipw2100_priv *priv, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = SYSTEM_CONFIG,
+ .host_command_sequence = 0,
+ .host_command_length = 12,
+ };
+ u32 ibss_mask, len = sizeof(u32);
+ int err;
+
+ /* Set system configuration */
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC)
+ cmd.host_command_parameters[0] |= IPW_CFG_IBSS_AUTO_START;
+
+ cmd.host_command_parameters[0] |= IPW_CFG_IBSS_MASK |
+ IPW_CFG_BSS_MASK |
+ IPW_CFG_802_1x_ENABLE;
+
+ if (!(priv->config & CFG_LONG_PREAMBLE))
+ cmd.host_command_parameters[0] |= IPW_CFG_PREAMBLE_AUTO;
+
+ err = ipw2100_get_ordinal(priv,
+ IPW_ORD_EEPROM_IBSS_11B_CHANNELS,
+ &ibss_mask, &len);
+ if (err)
+ ibss_mask = IPW_IBSS_11B_DEFAULT_MASK;
+
+ cmd.host_command_parameters[1] = REG_CHANNEL_MASK;
+ cmd.host_command_parameters[2] = REG_CHANNEL_MASK & ibss_mask;
+
+ /* 11b only */
+ /*cmd.host_command_parameters[0] |= DIVERSITY_ANTENNA_A;*/
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+/* If IPv6 is configured in the kernel then we don't want to filter out all
+ * of the multicast packets as IPv6 needs some. */
+#if !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE)
+ cmd.host_command = ADD_MULTICAST;
+ cmd.host_command_sequence = 0;
+ cmd.host_command_length = 0;
+
+ ipw2100_hw_send_command(priv, &cmd);
+#endif
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int ipw2100_set_tx_rates(struct ipw2100_priv *priv, u32 rate,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = BASIC_TX_RATES,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = rate & TX_RATE_MASK;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ /* Set BASIC TX Rate first */
+ ipw2100_hw_send_command(priv, &cmd);
+
+ /* Set TX Rate */
+ cmd.host_command = TX_RATES;
+ ipw2100_hw_send_command(priv, &cmd);
+
+ /* Set MSDU TX Rate */
+ cmd.host_command = MSDU_TX_RATES;
+ ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ priv->tx_rates = rate;
+
+ return 0;
+}
+
+static int ipw2100_set_power_mode(struct ipw2100_priv *priv,
+ int power_level)
+{
+ struct host_command cmd = {
+ .host_command = POWER_MODE,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = power_level;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ if (power_level == IPW_POWER_MODE_CAM)
+ priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
+ else
+ priv->power_mode = IPW_POWER_ENABLED | power_level;
+
+#ifdef CONFIG_IPW2100_TX_POWER
+ if (priv->port_type == IBSS &&
+ priv->adhoc_power != DFTL_IBSS_TX_POWER) {
+ /* Set beacon interval */
+ cmd.host_command = TX_POWER_INDEX;
+ cmd.host_command_parameters[0] = (u32)priv->adhoc_power;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+ }
+#endif
+
+ return 0;
+}
+
+
+static int ipw2100_set_rts_threshold(struct ipw2100_priv *priv, u32 threshold)
+{
+ struct host_command cmd = {
+ .host_command = RTS_THRESHOLD,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ if (threshold & RTS_DISABLED)
+ cmd.host_command_parameters[0] = MAX_RTS_THRESHOLD;
+ else
+ cmd.host_command_parameters[0] = threshold & ~RTS_DISABLED;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ priv->rts_threshold = threshold;
+
+ return 0;
+}
+
+#if 0
+int ipw2100_set_fragmentation_threshold(struct ipw2100_priv *priv,
+ u32 threshold, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = FRAG_THRESHOLD,
+ .host_command_sequence = 0,
+ .host_command_length = 4,
+ .host_command_parameters[0] = 0,
+ };
+ int err;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ if (threshold == 0)
+ threshold = DEFAULT_FRAG_THRESHOLD;
+ else {
+ threshold = max(threshold, MIN_FRAG_THRESHOLD);
+ threshold = min(threshold, MAX_FRAG_THRESHOLD);
+ }
+
+ cmd.host_command_parameters[0] = threshold;
+
+ IPW_DEBUG_HC("FRAG_THRESHOLD: %u\n", threshold);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ if (!err)
+ priv->frag_threshold = threshold;
+
+ return err;
+}
+#endif
+
+static int ipw2100_set_short_retry(struct ipw2100_priv *priv, u32 retry)
+{
+ struct host_command cmd = {
+ .host_command = SHORT_RETRY_LIMIT,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = retry;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ priv->short_retry_limit = retry;
+
+ return 0;
+}
+
+static int ipw2100_set_long_retry(struct ipw2100_priv *priv, u32 retry)
+{
+ struct host_command cmd = {
+ .host_command = LONG_RETRY_LIMIT,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = retry;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ priv->long_retry_limit = retry;
+
+ return 0;
+}
+
+
+static int ipw2100_set_mandatory_bssid(struct ipw2100_priv *priv, u8 *bssid,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = MANDATORY_BSSID,
+ .host_command_sequence = 0,
+ .host_command_length = (bssid == NULL) ? 0 : ETH_ALEN
+ };
+ int err;
+
+#ifdef CONFIG_IPW_DEBUG
+ if (bssid != NULL)
+ IPW_DEBUG_HC(
+ "MANDATORY_BSSID: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ bssid[0], bssid[1], bssid[2], bssid[3], bssid[4],
+ bssid[5]);
+ else
+ IPW_DEBUG_HC("MANDATORY_BSSID: <clear>\n");
+#endif
+ /* if BSSID is empty then we disable mandatory bssid mode */
+ if (bssid != NULL)
+ memcpy((u8 *)cmd.host_command_parameters, bssid, ETH_ALEN);
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+#ifdef CONFIG_IEEE80211_WPA
+static int ipw2100_disassociate_bssid(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = DISASSOCIATION_BSSID,
+ .host_command_sequence = 0,
+ .host_command_length = ETH_ALEN
+ };
+ int err;
+ int len;
+
+ IPW_DEBUG_HC("DISASSOCIATION_BSSID\n");
+
+ len = ETH_ALEN;
+ /* The Firmware currently ignores the BSSID and just disassociates from
+ * the currently associated AP -- but in the off chance that a future
+ * firmware does use the BSSID provided here, we go ahead and try and
+ * set it to the currently associated AP's BSSID */
+ memcpy(cmd.host_command_parameters, priv->bssid, ETH_ALEN);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ return err;
+}
+#endif
+
+/*
+ * Pseudo code for setting up wpa_frame:
+ */
+#if 0
+void x(struct ieee80211_assoc_frame *wpa_assoc)
+{
+ struct ipw2100_wpa_assoc_frame frame;
+ frame->fixed_ie_mask = IPW_WPA_CAPABILTIES |
+ IPW_WPA_LISTENINTERVAL |
+ IPW_WPA_AP_ADDRESS;
+ frame->capab_info = wpa_assoc->capab_info;
+ frame->lisen_interval = wpa_assoc->listent_interval;
+ memcpy(frame->current_ap, wpa_assoc->current_ap, ETH_ALEN);
+
+ /* UNKNOWN -- I'm not postivive about this part; don't have any WPA
+ * setup here to test it with.
+ *
+ * Walk the IEs in the wpa_assoc and figure out the total size of all
+ * that data. Stick that into frame->var_ie_len. Then memcpy() all of
+ * the IEs from wpa_frame into frame.
+ */
+ frame->var_ie_len = calculate_ie_len(wpa_assoc);
+ memcpy(frame->var_ie, wpa_assoc->variable, frame->var_ie_len);
+
+ ipw2100_set_wpa_ie(priv, &frame, 0);
+}
+#endif
+
+
+
+
+static int ipw2100_set_wpa_ie(struct ipw2100_priv *,
+ struct ipw2100_wpa_assoc_frame *, int)
+__attribute__ ((unused));
+
+static int ipw2100_set_wpa_ie(struct ipw2100_priv *priv,
+ struct ipw2100_wpa_assoc_frame *wpa_frame,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = SET_WPA_IE,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(struct ipw2100_wpa_assoc_frame),
+ };
+ int err;
+
+ IPW_DEBUG_HC("SET_WPA_IE\n");
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ memcpy(cmd.host_command_parameters, wpa_frame,
+ sizeof(struct ipw2100_wpa_assoc_frame));
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ if (ipw2100_enable_adapter(priv))
+ err = -EIO;
+ }
+
+ return err;
+}
+
+struct security_info_params {
+ u32 allowed_ciphers;
+ u16 version;
+ u8 auth_mode;
+ u8 replay_counters_number;
+ u8 unicast_using_group;
+} __attribute__ ((packed));
+
+static int ipw2100_set_security_information(struct ipw2100_priv *priv,
+ int auth_mode,
+ int security_level,
+ int unicast_using_group,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = SET_SECURITY_INFORMATION,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(struct security_info_params)
+ };
+ struct security_info_params *security =
+ (struct security_info_params *)&cmd.host_command_parameters;
+ int err;
+ memset(security, 0, sizeof(*security));
+
+ /* If shared key AP authentication is turned on, then we need to
+ * configure the firmware to try and use it.
+ *
+ * Actual data encryption/decryption is handled by the host. */
+ security->auth_mode = auth_mode;
+ security->unicast_using_group = unicast_using_group;
+
+ switch (security_level) {
+ default:
+ case SEC_LEVEL_0:
+ security->allowed_ciphers = IPW_NONE_CIPHER;
+ break;
+ case SEC_LEVEL_1:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER;
+ break;
+ case SEC_LEVEL_2:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER | IPW_TKIP_CIPHER;
+ break;
+ case SEC_LEVEL_2_CKIP:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER | IPW_CKIP_CIPHER;
+ break;
+ case SEC_LEVEL_3:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER | IPW_TKIP_CIPHER | IPW_CCMP_CIPHER;
+ break;
+ }
+
+ IPW_DEBUG_HC(
+ "SET_SECURITY_INFORMATION: auth:%d cipher:0x%02X (level %d)\n",
+ security->auth_mode, security->allowed_ciphers, security_level);
+
+ security->replay_counters_number = 0;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static int ipw2100_set_tx_power(struct ipw2100_priv *priv,
+ u32 tx_power)
+{
+ struct host_command cmd = {
+ .host_command = TX_POWER_INDEX,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err = 0;
+
+ cmd.host_command_parameters[0] = tx_power;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC)
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (!err)
+ priv->tx_power = tx_power;
+
+ return 0;
+}
+
+static int ipw2100_set_ibss_beacon_interval(struct ipw2100_priv *priv,
+ u32 interval, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = BEACON_INTERVAL,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = interval;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+
+void ipw2100_queues_initialize(struct ipw2100_priv *priv)
+{
+ ipw2100_tx_initialize(priv);
+ ipw2100_rx_initialize(priv);
+ ipw2100_msg_initialize(priv);
+}
+
+void ipw2100_queues_free(struct ipw2100_priv *priv)
+{
+ ipw2100_tx_free(priv);
+ ipw2100_rx_free(priv);
+ ipw2100_msg_free(priv);
+}
+
+int ipw2100_queues_allocate(struct ipw2100_priv *priv)
+{
+ if (ipw2100_tx_allocate(priv) ||
+ ipw2100_rx_allocate(priv) ||
+ ipw2100_msg_allocate(priv))
+ goto fail;
+
+ return 0;
+
+ fail:
+ ipw2100_tx_free(priv);
+ ipw2100_rx_free(priv);
+ ipw2100_msg_free(priv);
+ return -ENOMEM;
+}
+
+#define IPW_PRIVACY_CAPABLE 0x0008
+
+static int ipw2100_set_wep_flags(struct ipw2100_priv *priv, u32 flags,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = WEP_FLAGS,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = flags;
+
+ IPW_DEBUG_HC("WEP_FLAGS: flags = 0x%08X\n", flags);
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+struct ipw2100_wep_key {
+ u8 idx;
+ u8 len;
+ u8 key[13];
+};
+
+/* Macros to ease up priting WEP keys */
+#define WEP_FMT_64 "%02X%02X%02X%02X-%02X"
+#define WEP_FMT_128 "%02X%02X%02X%02X-%02X%02X%02X%02X-%02X%02X%02X"
+#define WEP_STR_64(x) x[0],x[1],x[2],x[3],x[4]
+#define WEP_STR_128(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10]
+
+
+/**
+ * Set a the wep key
+ *
+ * @priv: struct to work on
+ * @idx: index of the key we want to set
+ * @key: ptr to the key data to set
+ * @len: length of the buffer at @key
+ * @batch_mode: FIXME perform the operation in batch mode, not
+ * disabling the device.
+ *
+ * @returns 0 if OK, < 0 errno code on error.
+ *
+ * Fill out a command structure with the new wep key, length an
+ * index and send it down the wire.
+ */
+static int ipw2100_set_key(struct ipw2100_priv *priv,
+ int idx, char *key, int len, int batch_mode)
+{
+ int keylen = len ? (len <= 5 ? 5 : 13) : 0;
+ struct host_command cmd = {
+ .host_command = WEP_KEY_INFO,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(struct ipw2100_wep_key),
+ };
+ struct ipw2100_wep_key *wep_key = (void*)cmd.host_command_parameters;
+ int err;
+
+ IPW_DEBUG_HC("WEP_KEY_INFO: index = %d, len = %d/%d\n",
+ idx, keylen, len);
+
+ /* NOTE: We don't check cached values in case the firmware was reset
+ * or some other problem is occuring. If the user is setting the key,
+ * then we push the change */
+
+ wep_key->idx = idx;
+ wep_key->len = keylen;
+
+ if (keylen) {
+ memcpy(wep_key->key, key, len);
+ memset(wep_key->key + len, 0, keylen - len);
+ }
+
+ /* Will be optimized out on debug not being configured in */
+ if (keylen == 0)
+ IPW_DEBUG_WEP("%s: Clearing key %d\n",
+ priv->net_dev->name, wep_key->idx);
+ else if (keylen == 5)
+ IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_64 "\n",
+ priv->net_dev->name, wep_key->idx, wep_key->len,
+ WEP_STR_64(wep_key->key));
+ else
+ IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_128
+ "\n",
+ priv->net_dev->name, wep_key->idx, wep_key->len,
+ WEP_STR_128(wep_key->key));
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ /* FIXME: IPG: shouldn't this prink be in _disable_adapter()? */
+ if (err) {
+ printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ int err2 = ipw2100_enable_adapter(priv);
+ if (err == 0)
+ err = err2;
+ }
+ return err;
+}
+
+static int ipw2100_set_key_index(struct ipw2100_priv *priv,
+ int idx, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = WEP_KEY_INDEX,
+ .host_command_sequence = 0,
+ .host_command_length = 4,
+ .host_command_parameters = { idx },
+ };
+ int err;
+
+ IPW_DEBUG_HC("WEP_KEY_INDEX: index = %d\n", idx);
+
+ if (idx < 0 || idx > 3)
+ return -EINVAL;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+
+static int ipw2100_configure_security(struct ipw2100_priv *priv,
+ int batch_mode)
+{
+ int i, err, auth_mode, sec_level, use_group;
+
+ if (!(priv->status & STATUS_RUNNING))
+ return 0;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ if (!priv->sec.enabled) {
+ err = ipw2100_set_security_information(
+ priv, IPW_AUTH_OPEN, SEC_LEVEL_0, 0, 1);
+ } else {
+ auth_mode = IPW_AUTH_OPEN;
+ if ((priv->sec.flags & SEC_AUTH_MODE) &&
+ (priv->sec.auth_mode == WLAN_AUTH_SHARED_KEY))
+ auth_mode = IPW_AUTH_SHARED;
+
+ sec_level = SEC_LEVEL_0;
+ if (priv->sec.flags & SEC_LEVEL)
+ sec_level = priv->sec.level;
+
+ use_group = 0;
+ if (priv->sec.flags & SEC_UNICAST_GROUP)
+ use_group = priv->sec.unicast_uses_group;
+
+ err = ipw2100_set_security_information(
+ priv, auth_mode, sec_level, use_group, 1);
+ }
+
+ if (err)
+ goto exit;
+
+ if (priv->sec.enabled) {
+ for (i = 0; i < 4; i++) {
+ if (!(priv->sec.flags & (1 << i))) {
+ memset(priv->sec.keys[i], 0, WEP_KEY_LEN);
+ priv->sec.key_sizes[i] = 0;
+ } else {
+ err = ipw2100_set_key(priv, i,
+ priv->sec.keys[i],
+ priv->sec.key_sizes[i],
+ 1);
+ if (err)
+ goto exit;
+ }
+ }
+
+ ipw2100_set_key_index(priv, priv->ieee->tx_keyidx, 1);
+ }
+
+ /* Always enable privacy so the Host can filter WEP packets if
+ * encrypted data is sent up */
+ err = ipw2100_set_wep_flags(
+ priv, priv->sec.enabled ? IPW_PRIVACY_CAPABLE : 0, 1);
+ if (err)
+ goto exit;
+
+ priv->status &= ~STATUS_SECURITY_UPDATED;
+
+ exit:
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static void ipw2100_security_work(struct ipw2100_priv *priv)
+{
+ /* If we happen to have reconnected before we get a chance to
+ * process this, then update the security settings--which causes
+ * a disassociation to occur */
+ if (!(priv->status & STATUS_ASSOCIATED) &&
+ priv->status & STATUS_SECURITY_UPDATED)
+ ipw2100_configure_security(priv, 0);
+}
+
+static void shim__set_security(struct net_device *dev,
+ struct ieee80211_security *sec)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int i, force_update = 0;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED))
+ goto done;
+
+ for (i = 0; i < 4; i++) {
+ if (sec->flags & (1 << i)) {
+ priv->sec.key_sizes[i] = sec->key_sizes[i];
+ if (sec->key_sizes[i] == 0)
+ priv->sec.flags &= ~(1 << i);
+ else
+ memcpy(priv->sec.keys[i], sec->keys[i],
+ sec->key_sizes[i]);
+ priv->sec.flags |= (1 << i);
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+ }
+
+ if ((sec->flags & SEC_ACTIVE_KEY) &&
+ priv->sec.active_key != sec->active_key) {
+ if (sec->active_key <= 3) {
+ priv->sec.active_key = sec->active_key;
+ priv->sec.flags |= SEC_ACTIVE_KEY;
+ } else
+ priv->sec.flags &= ~SEC_ACTIVE_KEY;
+
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ if ((sec->flags & SEC_AUTH_MODE) &&
+ (priv->sec.auth_mode != sec->auth_mode)) {
+ priv->sec.auth_mode = sec->auth_mode;
+ priv->sec.flags |= SEC_AUTH_MODE;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ if (sec->flags & SEC_ENABLED &&
+ priv->sec.enabled != sec->enabled) {
+ priv->sec.flags |= SEC_ENABLED;
+ priv->sec.enabled = sec->enabled;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ force_update = 1;
+ }
+
+ if (sec->flags & SEC_LEVEL &&
+ priv->sec.level != sec->level) {
+ priv->sec.level = sec->level;
+ priv->sec.flags |= SEC_LEVEL;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ IPW_DEBUG_WEP("Security flags: %c %c%c%c%c %c%c%c%c\n",
+ priv->sec.flags & (1<<8) ? '1' : '0',
+ priv->sec.flags & (1<<7) ? '1' : '0',
+ priv->sec.flags & (1<<6) ? '1' : '0',
+ priv->sec.flags & (1<<5) ? '1' : '0',
+ priv->sec.flags & (1<<4) ? '1' : '0',
+ priv->sec.flags & (1<<3) ? '1' : '0',
+ priv->sec.flags & (1<<2) ? '1' : '0',
+ priv->sec.flags & (1<<1) ? '1' : '0',
+ priv->sec.flags & (1<<0) ? '1' : '0');
+
+/* As a temporary work around to enable WPA until we figure out why
+ * wpa_supplicant toggles the security capability of the driver, which
+ * forces a disassocation with force_update...
+ *
+ * if (force_update || !(priv->status & STATUS_ASSOCIATED))*/
+ if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
+ ipw2100_configure_security(priv, 0);
+done:
+ up(&priv->action_sem);
+}
+
+static int ipw2100_adapter_setup(struct ipw2100_priv *priv)
+{
+ int err;
+ int batch_mode = 1;
+ u8 *bssid;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+#ifdef CONFIG_IPW2100_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ err = ipw2100_set_channel(priv, priv->channel, batch_mode);
+ if (err)
+ return err;
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+ }
+#endif /* CONFIG_IPW2100_MONITOR */
+
+ err = ipw2100_read_mac_address(priv);
+ if (err)
+ return -EIO;
+
+ err = ipw2100_set_mac_address(priv, batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_set_port_type(priv, priv->ieee->iw_mode, batch_mode);
+ if (err)
+ return err;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ err = ipw2100_set_channel(priv, priv->channel, batch_mode);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_system_config(priv, batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_set_tx_rates(priv, priv->tx_rates, batch_mode);
+ if (err)
+ return err;
+
+ /* Default to power mode OFF */
+ err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
+ if (err)
+ return err;
+
+ err = ipw2100_set_rts_threshold(priv, priv->rts_threshold);
+ if (err)
+ return err;
+
+ if (priv->config & CFG_STATIC_BSSID)
+ bssid = priv->bssid;
+ else
+ bssid = NULL;
+ err = ipw2100_set_mandatory_bssid(priv, bssid, batch_mode);
+ if (err)
+ return err;
+
+ if (priv->config & CFG_STATIC_ESSID)
+ err = ipw2100_set_essid(priv, priv->essid, priv->essid_len,
+ batch_mode);
+ else
+ err = ipw2100_set_essid(priv, NULL, 0, batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_configure_security(priv, batch_mode);
+ if (err)
+ return err;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ err = ipw2100_set_ibss_beacon_interval(
+ priv, priv->beacon_interval, batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_set_tx_power(priv, priv->tx_power);
+ if (err)
+ return err;
+ }
+
+ /*
+ err = ipw2100_set_fragmentation_threshold(
+ priv, priv->frag_threshold, batch_mode);
+ if (err)
+ return err;
+ */
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+
+/*************************************************************************
+ *
+ * EXTERNALLY CALLED METHODS
+ *
+ *************************************************************************/
+
+/* This method is called by the network layer -- not to be confused with
+ * ipw2100_set_mac_address() declared above called by this driver (and this
+ * method as well) to talk to the firmware */
+static int ipw2100_set_address(struct net_device *dev, void *p)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct sockaddr *addr = p;
+ int err = 0;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ down(&priv->action_sem);
+
+ priv->config |= CFG_CUSTOM_MAC;
+ memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
+
+ err = ipw2100_set_mac_address(priv, 0);
+ if (err)
+ goto done;
+
+ priv->reset_backoff = 0;
+ up(&priv->action_sem);
+ ipw2100_reset_adapter(priv);
+ return 0;
+
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+static int ipw2100_open(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ unsigned long flags;
+ IPW_DEBUG_INFO("dev->open\n");
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+ if (priv->status & STATUS_ASSOCIATED) {
+ netif_carrier_on(dev);
+ netif_start_queue(dev);
+ }
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ return 0;
+}
+
+static int ipw2100_close(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ unsigned long flags;
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (priv->status & STATUS_ASSOCIATED)
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+
+ /* Flush the TX queue ... */
+ while (!list_empty(&priv->tx_pend_list)) {
+ element = priv->tx_pend_list.next;
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+ list_del(element);
+ DEC_STAT(&priv->tx_pend_stat);
+
+ ieee80211_txb_free(packet->info.d_struct.txb);
+ packet->info.d_struct.txb = NULL;
+
+ list_add_tail(element, &priv->tx_free_list);
+ INC_STAT(&priv->tx_free_stat);
+ }
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+
+
+/*
+ * TODO: Fix this function... its just wrong
+ */
+static void ipw2100_tx_timeout(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ priv->ieee->stats.tx_errors++;
+
+#ifdef CONFIG_IPW2100_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+ return;
+#endif
+
+ IPW_DEBUG_INFO("%s: TX timed out. Scheduling firmware restart.\n",
+ dev->name);
+ schedule_reset(priv);
+}
+
+
+/*
+ * TODO: reimplement it so that it reads statistics
+ * from the adapter using ordinal tables
+ * instead of/in addition to collecting them
+ * in the driver
+ */
+static struct net_device_stats *ipw2100_stats(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ return &priv->ieee->stats;
+}
+
+/* Support for wpa_supplicant. Will be replaced with WEXT once
+ * they get WPA support. */
+#ifdef CONFIG_IEEE80211_WPA
+
+/* following definitions must match definitions in driver_ipw2100.c */
+
+#define IPW2100_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30
+
+#define IPW2100_CMD_SET_WPA_PARAM 1
+#define IPW2100_CMD_SET_WPA_IE 2
+#define IPW2100_CMD_SET_ENCRYPTION 3
+#define IPW2100_CMD_MLME 4
+
+#define IPW2100_PARAM_WPA_ENABLED 1
+#define IPW2100_PARAM_TKIP_COUNTERMEASURES 2
+#define IPW2100_PARAM_DROP_UNENCRYPTED 3
+#define IPW2100_PARAM_PRIVACY_INVOKED 4
+#define IPW2100_PARAM_AUTH_ALGS 5
+#define IPW2100_PARAM_IEEE_802_1X 6
+
+#define IPW2100_MLME_STA_DEAUTH 1
+#define IPW2100_MLME_STA_DISASSOC 2
+
+#define IPW2100_CRYPT_ERR_UNKNOWN_ALG 2
+#define IPW2100_CRYPT_ERR_UNKNOWN_ADDR 3
+#define IPW2100_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define IPW2100_CRYPT_ERR_KEY_SET_FAILED 5
+#define IPW2100_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define IPW2100_CRYPT_ERR_CARD_CONF_FAILED 7
+
+#define IPW2100_CRYPT_ALG_NAME_LEN 16
+
+struct ipw2100_param {
+ u32 cmd;
+ u8 sta_addr[ETH_ALEN];
+ union {
+ struct {
+ u8 name;
+ u32 value;
+ } wpa_param;
+ struct {
+ u32 len;
+ u8 *data;
+ } wpa_ie;
+ struct{
+ int command;
+ int reason_code;
+ } mlme;
+ struct {
+ u8 alg[IPW2100_CRYPT_ALG_NAME_LEN];
+ u8 set_tx;
+ u32 err;
+ u8 idx;
+ u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+ u16 key_len;
+ u8 key[0];
+ } crypt;
+
+ } u;
+};
+
+/* end of driver_ipw2100.c code */
+
+static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value){
+
+ struct ieee80211_device *ieee = priv->ieee;
+ struct ieee80211_security sec = {
+ .flags = SEC_LEVEL | SEC_ENABLED,
+ };
+ int ret = 0;
+
+ ieee->wpa_enabled = value;
+
+ if (value){
+ sec.level = SEC_LEVEL_3;
+ sec.enabled = 1;
+ } else {
+ sec.level = SEC_LEVEL_0;
+ sec.enabled = 0;
+ }
+
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+ else
+ ret = -EOPNOTSUPP;
+
+ return ret;
+}
+
+#define AUTH_ALG_OPEN_SYSTEM 0x1
+#define AUTH_ALG_SHARED_KEY 0x2
+
+static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value){
+
+ struct ieee80211_device *ieee = priv->ieee;
+ struct ieee80211_security sec = {
+ .flags = SEC_AUTH_MODE,
+ };
+ int ret = 0;
+
+ if (value & AUTH_ALG_SHARED_KEY){
+ sec.auth_mode = WLAN_AUTH_SHARED_KEY;
+ ieee->open_wep = 0;
+ } else {
+ sec.auth_mode = WLAN_AUTH_OPEN;
+ ieee->open_wep = 1;
+ }
+
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+ else
+ ret = -EOPNOTSUPP;
+
+ return ret;
+}
+
+
+static int ipw2100_wpa_set_param(struct net_device *dev, u8 name, u32 value){
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int ret=0;
+
+ switch(name){
+ case IPW2100_PARAM_WPA_ENABLED:
+ ret = ipw2100_wpa_enable(priv, value);
+ break;
+
+ case IPW2100_PARAM_TKIP_COUNTERMEASURES:
+ priv->ieee->tkip_countermeasures=value;
+ break;
+
+ case IPW2100_PARAM_DROP_UNENCRYPTED:
+ priv->ieee->drop_unencrypted=value;
+ break;
+
+ case IPW2100_PARAM_PRIVACY_INVOKED:
+ priv->ieee->privacy_invoked=value;
+ break;
+
+ case IPW2100_PARAM_AUTH_ALGS:
+ ret = ipw2100_wpa_set_auth_algs(priv, value);
+ break;
+
+ case IPW2100_PARAM_IEEE_802_1X:
+ priv->ieee->ieee802_1x=value;
+ break;
+
+ default:
+ printk(KERN_ERR DRV_NAME ": %s: Unknown WPA param: %d\n",
+ dev->name, name);
+ ret = -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+static int ipw2100_wpa_mlme(struct net_device *dev, int command, int reason){
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int ret=0;
+
+ switch(command){
+ case IPW2100_MLME_STA_DEAUTH:
+ // silently ignore
+ break;
+
+ case IPW2100_MLME_STA_DISASSOC:
+ ipw2100_disassociate_bssid(priv);
+ break;
+
+ default:
+ printk(KERN_ERR DRV_NAME ": %s: Unknown MLME request: %d\n",
+ dev->name, command);
+ ret = -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+
+void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv,
+ char *wpa_ie, int wpa_ie_len){
+
+ struct ipw2100_wpa_assoc_frame frame;
+
+ frame.fixed_ie_mask = 0;
+
+ /* copy WPA IE */
+ memcpy(frame.var_ie, wpa_ie, wpa_ie_len);
+ frame.var_ie_len = wpa_ie_len;
+
+ /* make sure WPA is enabled */
+ ipw2100_wpa_enable(priv, 1);
+ ipw2100_set_wpa_ie(priv, &frame, 0);
+}
+
+
+static int ipw2100_wpa_set_wpa_ie(struct net_device *dev,
+ struct ipw2100_param *param, int plen){
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee;
+ u8 *buf;
+
+ if (! ieee->wpa_enabled)
+ return -EOPNOTSUPP;
+
+ if (param->u.wpa_ie.len > MAX_WPA_IE_LEN ||
+ (param->u.wpa_ie.len &&
+ param->u.wpa_ie.data==NULL))
+ return -EINVAL;
+
+ if (param->u.wpa_ie.len){
+ buf = kmalloc(param->u.wpa_ie.len, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ memcpy(buf, param->u.wpa_ie.data, param->u.wpa_ie.len);
+
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = buf;
+ ieee->wpa_ie_len = param->u.wpa_ie.len;
+
+ } else {
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = NULL;
+ ieee->wpa_ie_len = 0;
+ }
+
+ ipw2100_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
+
+ return 0;
+}
+
+/* implementation borrowed from hostap driver */
+
+static int ipw2100_wpa_set_encryption(struct net_device *dev,
+ struct ipw2100_param *param, int param_len){
+
+ int ret = 0;
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee;
+ struct ieee80211_crypto_ops *ops;
+ struct ieee80211_crypt_data **crypt;
+
+ struct ieee80211_security sec = {
+ .flags = 0,
+ };
+
+ param->u.crypt.err = 0;
+ param->u.crypt.alg[IPW2100_CRYPT_ALG_NAME_LEN - 1] = '\0';
+
+ if (param_len !=
+ (int) ((char *) param->u.crypt.key - (char *) param) +
+ param->u.crypt.key_len){
+ IPW_DEBUG_INFO("Len mismatch %d, %d\n", param_len, param->u.crypt.key_len);
+ return -EINVAL;
+ }
+ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (param->u.crypt.idx >= WEP_KEYS)
+ return -EINVAL;
+ crypt = &ieee->crypt[param->u.crypt.idx];
+ } else {
+ return -EINVAL;
+ }
+
+ if (strcmp(param->u.crypt.alg, "none") == 0) {
+ if (crypt){
+ sec.enabled = 0;
+ sec.level = SEC_LEVEL_0;
+ sec.flags |= SEC_ENABLED | SEC_LEVEL;
+ ieee80211_crypt_delayed_deinit(ieee, crypt);
+ }
+ goto done;
+ }
+ sec.enabled = 1;
+ sec.flags |= SEC_ENABLED;
+
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) {
+ request_module("ieee80211_crypt_wep");
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) {
+ request_module("ieee80211_crypt_tkip");
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) {
+ request_module("ieee80211_crypt_ccmp");
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ }
+ if (ops == NULL) {
+ IPW_DEBUG_INFO("%s: unknown crypto alg '%s'\n",
+ dev->name, param->u.crypt.alg);
+ param->u.crypt.err = IPW2100_CRYPT_ERR_UNKNOWN_ALG;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (*crypt == NULL || (*crypt)->ops != ops) {
+ struct ieee80211_crypt_data *new_crypt;
+
+ ieee80211_crypt_delayed_deinit(ieee, crypt);
+
+ new_crypt = (struct ieee80211_crypt_data *)
+ kmalloc(sizeof(struct ieee80211_crypt_data), GFP_KERNEL);
+ if (new_crypt == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
+ new_crypt->ops = ops;
+ if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
+ new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx);
+
+ if (new_crypt->priv == NULL) {
+ kfree(new_crypt);
+ param->u.crypt.err =
+ IPW2100_CRYPT_ERR_CRYPT_INIT_FAILED;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ *crypt = new_crypt;
+ }
+
+ if (param->u.crypt.key_len > 0 && (*crypt)->ops->set_key &&
+ (*crypt)->ops->set_key(param->u.crypt.key,
+ param->u.crypt.key_len, param->u.crypt.seq,
+ (*crypt)->priv) < 0) {
+ IPW_DEBUG_INFO("%s: key setting failed\n",
+ dev->name);
+ param->u.crypt.err = IPW2100_CRYPT_ERR_KEY_SET_FAILED;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (param->u.crypt.set_tx){
+ ieee->tx_keyidx = param->u.crypt.idx;
+ sec.active_key = param->u.crypt.idx;
+ sec.flags |= SEC_ACTIVE_KEY;
+ }
+
+ if (ops->name != NULL){
+
+ if (strcmp(ops->name, "WEP") == 0) {
+ memcpy(sec.keys[param->u.crypt.idx], param->u.crypt.key, param->u.crypt.key_len);
+ sec.key_sizes[param->u.crypt.idx] = param->u.crypt.key_len;
+ sec.flags |= (1 << param->u.crypt.idx);
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1;
+ } else if (strcmp(ops->name, "TKIP") == 0) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_2;
+ } else if (strcmp(ops->name, "CCMP") == 0) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_3;
+ }
+ }
+ done:
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+
+ /* Do not reset port if card is in Managed mode since resetting will
+ * generate new IEEE 802.11 authentication which may end up in looping
+ * with IEEE 802.1X. If your hardware requires a reset after WEP
+ * configuration (for example... Prism2), implement the reset_port in
+ * the callbacks structures used to initialize the 802.11 stack. */
+ if (ieee->reset_on_keychange &&
+ ieee->iw_mode != IW_MODE_INFRA &&
+ ieee->reset_port &&
+ ieee->reset_port(dev)) {
+ IPW_DEBUG_INFO("%s: reset_port failed\n", dev->name);
+ param->u.crypt.err = IPW2100_CRYPT_ERR_CARD_CONF_FAILED;
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+
+static int ipw2100_wpa_supplicant(struct net_device *dev, struct iw_point *p){
+
+ struct ipw2100_param *param;
+ int ret=0;
+
+ IPW_DEBUG_IOCTL("wpa_supplicant: len=%d\n", p->length);
+
+ if (p->length < sizeof(struct ipw2100_param) || !p->pointer)
+ return -EINVAL;
+
+ param = (struct ipw2100_param *)kmalloc(p->length, GFP_KERNEL);
+ if (param == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(param, p->pointer, p->length)){
+ kfree(param);
+ return -EFAULT;
+ }
+
+ switch (param->cmd){
+
+ case IPW2100_CMD_SET_WPA_PARAM:
+ ret = ipw2100_wpa_set_param(dev, param->u.wpa_param.name,
+ param->u.wpa_param.value);
+ break;
+
+ case IPW2100_CMD_SET_WPA_IE:
+ ret = ipw2100_wpa_set_wpa_ie(dev, param, p->length);
+ break;
+
+ case IPW2100_CMD_SET_ENCRYPTION:
+ ret = ipw2100_wpa_set_encryption(dev, param, p->length);
+ break;
+
+ case IPW2100_CMD_MLME:
+ ret = ipw2100_wpa_mlme(dev, param->u.mlme.command,
+ param->u.mlme.reason_code);
+ break;
+
+ default:
+ printk(KERN_ERR DRV_NAME ": %s: Unknown WPA supplicant request: %d\n",
+ dev->name, param->cmd);
+ ret = -EOPNOTSUPP;
+
+ }
+
+ if (ret == 0 && copy_to_user(p->pointer, param, p->length))
+ ret = -EFAULT;
+
+ kfree(param);
+ return ret;
+}
+#endif /* CONFIG_IEEE80211_WPA */
+
+static int ipw2100_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+#ifdef CONFIG_IEEE80211_WPA
+ struct iwreq *wrq = (struct iwreq *) rq;
+ int ret=-1;
+ switch (cmd){
+ case IPW2100_IOCTL_WPA_SUPPLICANT:
+ ret = ipw2100_wpa_supplicant(dev, &wrq->u.data);
+ return ret;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+#endif /* CONFIG_IEEE80211_WPA */
+
+ return -EOPNOTSUPP;
+}
+
+
+static void ipw_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ char fw_ver[64], ucode_ver[64];
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+
+ ipw2100_get_fwversion(priv, fw_ver, sizeof(fw_ver));
+ ipw2100_get_ucodeversion(priv, ucode_ver, sizeof(ucode_ver));
+
+ snprintf(info->fw_version, sizeof(info->fw_version), "%s:%d:%s",
+ fw_ver, priv->eeprom_version, ucode_ver);
+
+ strcpy(info->bus_info, pci_name(priv->pci_dev));
+}
+
+static u32 ipw2100_ethtool_get_link(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return (priv->status & STATUS_ASSOCIATED) ? 1 : 0;
+}
+
+
+static struct ethtool_ops ipw2100_ethtool_ops = {
+ .get_link = ipw2100_ethtool_get_link,
+ .get_drvinfo = ipw_ethtool_get_drvinfo,
+};
+
+static void ipw2100_hang_check(void *adapter)
+{
+ struct ipw2100_priv *priv = adapter;
+ unsigned long flags;
+ u32 rtc = 0xa5a5a5a5;
+ u32 len = sizeof(rtc);
+ int restart = 0;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (priv->fatal_error != 0) {
+ /* If fatal_error is set then we need to restart */
+ IPW_DEBUG_INFO("%s: Hardware fatal error detected.\n",
+ priv->net_dev->name);
+
+ restart = 1;
+ } else if (ipw2100_get_ordinal(priv, IPW_ORD_RTC_TIME, &rtc, &len) ||
+ (rtc == priv->last_rtc)) {
+ /* Check if firmware is hung */
+ IPW_DEBUG_INFO("%s: Firmware RTC stalled.\n",
+ priv->net_dev->name);
+
+ restart = 1;
+ }
+
+ if (restart) {
+ /* Kill timer */
+ priv->stop_hang_check = 1;
+ priv->hangs++;
+
+ /* Restart the NIC */
+ schedule_reset(priv);
+ }
+
+ priv->last_rtc = rtc;
+
+ if (!priv->stop_hang_check)
+ queue_delayed_work(priv->workqueue, &priv->hang_check, HZ / 2);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+}
+
+
+static void ipw2100_rf_kill(void *adapter)
+{
+ struct ipw2100_priv *priv = adapter;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
+ if (!priv->stop_rf_kill)
+ queue_delayed_work(priv->workqueue, &priv->rf_kill, HZ);
+ goto exit_unlock;
+ }
+
+ /* RF Kill is now disabled, so bring the device back up */
+
+ if (!(priv->status & STATUS_RF_KILL_MASK)) {
+ IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
+ "device\n");
+ schedule_reset(priv);
+ } else
+ IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
+ "enabled\n");
+
+ exit_unlock:
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+}
+
+static void ipw2100_irq_tasklet(struct ipw2100_priv *priv);
+
+/* Look into using netdev destructor to shutdown ieee80211? */
+
+static struct net_device *ipw2100_alloc_device(
+ struct pci_dev *pci_dev,
+ char *base_addr,
+ unsigned long mem_start,
+ unsigned long mem_len)
+{
+ struct ipw2100_priv *priv;
+ struct net_device *dev;
+
+ dev = alloc_ieee80211(sizeof(struct ipw2100_priv));
+ if (!dev)
+ return NULL;
+ priv = ieee80211_priv(dev);
+ priv->ieee = netdev_priv(dev);
+ priv->pci_dev = pci_dev;
+ priv->net_dev = dev;
+
+ priv->ieee->hard_start_xmit = ipw2100_tx;
+ priv->ieee->set_security = shim__set_security;
+
+ dev->open = ipw2100_open;
+ dev->stop = ipw2100_close;
+ dev->init = ipw2100_net_init;
+ dev->do_ioctl = ipw2100_ioctl;
+ dev->get_stats = ipw2100_stats;
+ dev->ethtool_ops = &ipw2100_ethtool_ops;
+ dev->tx_timeout = ipw2100_tx_timeout;
+ dev->wireless_handlers = &ipw2100_wx_handler_def;
+ dev->get_wireless_stats = ipw2100_wx_wireless_stats;
+ dev->set_mac_address = ipw2100_set_address;
+ dev->watchdog_timeo = 3*HZ;
+ dev->irq = 0;
+
+ dev->base_addr = (unsigned long)base_addr;
+ dev->mem_start = mem_start;
+ dev->mem_end = dev->mem_start + mem_len - 1;
+
+ /* NOTE: We don't use the wireless_handlers hook
+ * in dev as the system will start throwing WX requests
+ * to us before we're actually initialized and it just
+ * ends up causing problems. So, we just handle
+ * the WX extensions through the ipw2100_ioctl interface */
+
+
+ /* memset() puts everything to 0, so we only have explicitely set
+ * those values that need to be something else */
+
+ /* If power management is turned on, default to AUTO mode */
+ priv->power_mode = IPW_POWER_AUTO;
+
+
+
+#ifdef CONFIG_IEEE80211_WPA
+ priv->ieee->wpa_enabled = 0;
+ priv->ieee->tkip_countermeasures = 0;
+ priv->ieee->drop_unencrypted = 0;
+ priv->ieee->privacy_invoked = 0;
+ priv->ieee->ieee802_1x = 1;
+#endif /* CONFIG_IEEE80211_WPA */
+
+ /* Set module parameters */
+ switch (mode) {
+ case 1:
+ priv->ieee->iw_mode = IW_MODE_ADHOC;
+ break;
+#ifdef CONFIG_IPW2100_MONITOR
+ case 2:
+ priv->ieee->iw_mode = IW_MODE_MONITOR;
+ break;
+#endif
+ default:
+ case 0:
+ priv->ieee->iw_mode = IW_MODE_INFRA;
+ break;
+ }
+
+ if (disable == 1)
+ priv->status |= STATUS_RF_KILL_SW;
+
+ if (channel != 0 &&
+ ((channel >= REG_MIN_CHANNEL) &&
+ (channel <= REG_MAX_CHANNEL))) {
+ priv->config |= CFG_STATIC_CHANNEL;
+ priv->channel = channel;
+ }
+
+ if (associate)
+ priv->config |= CFG_ASSOCIATE;
+
+ priv->beacon_interval = DEFAULT_BEACON_INTERVAL;
+ priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
+ priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
+ priv->rts_threshold = DEFAULT_RTS_THRESHOLD | RTS_DISABLED;
+ priv->frag_threshold = DEFAULT_FTS | FRAG_DISABLED;
+ priv->tx_power = IPW_TX_POWER_DEFAULT;
+ priv->tx_rates = DEFAULT_TX_RATES;
+
+ strcpy(priv->nick, "ipw2100");
+
+ spin_lock_init(&priv->low_lock);
+ sema_init(&priv->action_sem, 1);
+ sema_init(&priv->adapter_sem, 1);
+
+ init_waitqueue_head(&priv->wait_command_queue);
+
+ netif_carrier_off(dev);
+
+ INIT_LIST_HEAD(&priv->msg_free_list);
+ INIT_LIST_HEAD(&priv->msg_pend_list);
+ INIT_STAT(&priv->msg_free_stat);
+ INIT_STAT(&priv->msg_pend_stat);
+
+ INIT_LIST_HEAD(&priv->tx_free_list);
+ INIT_LIST_HEAD(&priv->tx_pend_list);
+ INIT_STAT(&priv->tx_free_stat);
+ INIT_STAT(&priv->tx_pend_stat);
+
+ INIT_LIST_HEAD(&priv->fw_pend_list);
+ INIT_STAT(&priv->fw_pend_stat);
+
+
+#ifdef CONFIG_SOFTWARE_SUSPEND2
+ priv->workqueue = create_workqueue(DRV_NAME, 0);
+#else
+ priv->workqueue = create_workqueue(DRV_NAME);
+#endif
+ INIT_WORK(&priv->reset_work,
+ (void (*)(void *))ipw2100_reset_adapter, priv);
+ INIT_WORK(&priv->security_work,
+ (void (*)(void *))ipw2100_security_work, priv);
+ INIT_WORK(&priv->wx_event_work,
+ (void (*)(void *))ipw2100_wx_event_work, priv);
+ INIT_WORK(&priv->hang_check, ipw2100_hang_check, priv);
+ INIT_WORK(&priv->rf_kill, ipw2100_rf_kill, priv);
+
+ tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+ ipw2100_irq_tasklet, (unsigned long)priv);
+
+ /* NOTE: We do not start the deferred work for status checks yet */
+ priv->stop_rf_kill = 1;
+ priv->stop_hang_check = 1;
+
+ return dev;
+}
+
+static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
+ const struct pci_device_id *ent)
+{
+ unsigned long mem_start, mem_len, mem_flags;
+ char *base_addr = NULL;
+ struct net_device *dev = NULL;
+ struct ipw2100_priv *priv = NULL;
+ int err = 0;
+ int registered = 0;
+ u32 val;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ mem_start = pci_resource_start(pci_dev, 0);
+ mem_len = pci_resource_len(pci_dev, 0);
+ mem_flags = pci_resource_flags(pci_dev, 0);
+
+ if ((mem_flags & IORESOURCE_MEM) != IORESOURCE_MEM) {
+ IPW_DEBUG_INFO("weird - resource type is not memory\n");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ base_addr = ioremap_nocache(mem_start, mem_len);
+ if (!base_addr) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling ioremap_nocache.\n");
+ err = -EIO;
+ goto fail;
+ }
+
+ /* allocate and initialize our net_device */
+ dev = ipw2100_alloc_device(pci_dev, base_addr, mem_start, mem_len);
+ if (!dev) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling ipw2100_alloc_device.\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* set up PCI mappings for device */
+ err = pci_enable_device(pci_dev);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling pci_enable_device.\n");
+ return err;
+ }
+
+ priv = ieee80211_priv(dev);
+
+ pci_set_master(pci_dev);
+ pci_set_drvdata(pci_dev, priv);
+
+ err = pci_set_dma_mask(pci_dev, DMA_32BIT_MASK);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling pci_set_dma_mask.\n");
+ pci_disable_device(pci_dev);
+ return err;
+ }
+
+ err = pci_request_regions(pci_dev, DRV_NAME);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling pci_request_regions.\n");
+ pci_disable_device(pci_dev);
+ return err;
+ }
+
+ /* We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state */
+ pci_read_config_dword(pci_dev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
+
+ pci_set_power_state(pci_dev, PCI_D0);
+
+ if (!ipw2100_hw_is_adapter_in_system(dev)) {
+ printk(KERN_WARNING DRV_NAME
+ "Device not found via register read.\n");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ SET_NETDEV_DEV(dev, &pci_dev->dev);
+
+ /* Force interrupts to be shut off on the device */
+ priv->status |= STATUS_INT_ENABLED;
+ ipw2100_disable_interrupts(priv);
+
+ /* Allocate and initialize the Tx/Rx queues and lists */
+ if (ipw2100_queues_allocate(priv)) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calilng ipw2100_queues_allocate.\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+ ipw2100_queues_initialize(priv);
+
+ err = request_irq(pci_dev->irq,
+ ipw2100_interrupt, SA_SHIRQ,
+ dev->name, priv);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling request_irq: %d.\n",
+ pci_dev->irq);
+ goto fail;
+ }
+ dev->irq = pci_dev->irq;
+
+ IPW_DEBUG_INFO("Attempting to register device...\n");
+
+ SET_MODULE_OWNER(dev);
+
+ printk(KERN_INFO DRV_NAME
+ ": Detected Intel PRO/Wireless 2100 Network Connection\n");
+
+ /* Bring up the interface. Pre 0.46, after we registered the
+ * network device we would call ipw2100_up. This introduced a race
+ * condition with newer hotplug configurations (network was coming
+ * up and making calls before the device was initialized).
+ *
+ * If we called ipw2100_up before we registered the device, then the
+ * device name wasn't registered. So, we instead use the net_dev->init
+ * member to call a function that then just turns and calls ipw2100_up.
+ * net_dev->init is called after name allocation but before the
+ * notifier chain is called */
+ down(&priv->action_sem);
+ err = register_netdev(dev);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling register_netdev.\n");
+ goto fail_unlock;
+ }
+ registered = 1;
+
+ IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev));
+
+ /* perform this after register_netdev so that dev->name is set */
+ sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+ netif_carrier_off(dev);
+
+ /* If the RF Kill switch is disabled, go ahead and complete the
+ * startup sequence */
+ if (!(priv->status & STATUS_RF_KILL_MASK)) {
+ /* Enable the adapter - sends HOST_COMPLETE */
+ if (ipw2100_enable_adapter(priv)) {
+ printk(KERN_WARNING DRV_NAME
+ ": %s: failed in call to enable adapter.\n",
+ priv->net_dev->name);
+ ipw2100_hw_stop_adapter(priv);
+ err = -EIO;
+ goto fail_unlock;
+ }
+
+ /* Start a scan . . . */
+ ipw2100_set_scan_options(priv);
+ ipw2100_start_scan(priv);
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+
+ priv->status |= STATUS_INITIALIZED;
+
+ up(&priv->action_sem);
+
+ return 0;
+
+ fail_unlock:
+ up(&priv->action_sem);
+
+ fail:
+ if (dev) {
+ if (registered)
+ unregister_netdev(dev);
+
+ ipw2100_hw_stop_adapter(priv);
+
+ ipw2100_disable_interrupts(priv);
+
+ if (dev->irq)
+ free_irq(dev->irq, priv);
+
+ ipw2100_kill_workqueue(priv);
+
+ /* These are safe to call even if they weren't allocated */
+ ipw2100_queues_free(priv);
+ sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+
+ free_ieee80211(dev);
+ pci_set_drvdata(pci_dev, NULL);
+ }
+
+ if (base_addr)
+ iounmap((char*)base_addr);
+
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
+
+ return err;
+}
+
+static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev)
+{
+ struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+ struct net_device *dev;
+
+ if (priv) {
+ down(&priv->action_sem);
+
+ priv->status &= ~STATUS_INITIALIZED;
+
+ dev = priv->net_dev;
+ sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+
+#ifdef CONFIG_PM
+ if (ipw2100_firmware.version)
+ ipw2100_release_firmware(priv, &ipw2100_firmware);
+#endif
+ /* Take down the hardware */
+ ipw2100_down(priv);
+
+ /* Release the semaphore so that the network subsystem can
+ * complete any needed calls into the driver... */
+ up(&priv->action_sem);
+
+ /* Unregister the device first - this results in close()
+ * being called if the device is open. If we free storage
+ * first, then close() will crash. */
+ unregister_netdev(dev);
+
+ /* ipw2100_down will ensure that there is no more pending work
+ * in the workqueue's, so we can safely remove them now. */
+ ipw2100_kill_workqueue(priv);
+
+ ipw2100_queues_free(priv);
+
+ /* Free potential debugging firmware snapshot */
+ ipw2100_snapshot_free(priv);
+
+ if (dev->irq)
+ free_irq(dev->irq, priv);
+
+ if (dev->base_addr)
+ iounmap((unsigned char *)dev->base_addr);
+
+ free_ieee80211(dev);
+ }
+
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+
+#ifdef CONFIG_PM
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+static int ipw2100_suspend(struct pci_dev *pci_dev, u32 state)
+#else
+static int ipw2100_suspend(struct pci_dev *pci_dev, pm_message_t state)
+#endif
+{
+ struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+ struct net_device *dev = priv->net_dev;
+
+ IPW_DEBUG_INFO("%s: Going into suspend...\n",
+ dev->name);
+
+ down(&priv->action_sem);
+ if (priv->status & STATUS_INITIALIZED) {
+ /* Take down the device; powers it off, etc. */
+ ipw2100_down(priv);
+ }
+
+ /* Remove the PRESENT state of the device */
+ netif_device_detach(dev);
+
+ pci_save_state(pci_dev);
+ pci_disable_device (pci_dev);
+ pci_set_power_state(pci_dev, PCI_D3hot);
+
+ up(&priv->action_sem);
+
+ return 0;
+}
+
+static int ipw2100_resume(struct pci_dev *pci_dev)
+{
+ struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+ struct net_device *dev = priv->net_dev;
+ u32 val;
+
+ if (IPW2100_PM_DISABLED)
+ return 0;
+
+ down(&priv->action_sem);
+
+ IPW_DEBUG_INFO("%s: Coming out of suspend...\n",
+ dev->name);
+
+ pci_set_power_state(pci_dev, PCI_D0);
+ pci_enable_device(pci_dev);
+ pci_restore_state(pci_dev);
+
+ /*
+ * Suspend/Resume resets the PCI configuration space, so we have to
+ * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
+ * from interfering with C3 CPU state. pci_restore_state won't help
+ * here since it only restores the first 64 bytes pci config header.
+ */
+ pci_read_config_dword(pci_dev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
+
+ /* Set the device back into the PRESENT state; this will also wake
+ * the queue of needed */
+ netif_device_attach(dev);
+
+ /* Bring the device back up */
+ if (!(priv->status & STATUS_RF_KILL_SW))
+ ipw2100_up(priv, 0);
+
+ up(&priv->action_sem);
+
+ return 0;
+}
+#endif
+
+
+#define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x }
+
+static struct pci_device_id ipw2100_pci_id_table[] __devinitdata = {
+ IPW2100_DEV_ID(0x2520), /* IN 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2521), /* IN 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2524), /* IN 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2525), /* IN 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2526), /* IN 2100A mPCI Gen A3 */
+ IPW2100_DEV_ID(0x2522), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2523), /* IN 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2527), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2528), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2529), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x252B), /* IN 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x252C), /* IN 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x252D), /* IN 2100 mPCI 3A */
+
+ IPW2100_DEV_ID(0x2550), /* IB 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2551), /* IB 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2553), /* IB 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2554), /* IB 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2555), /* IB 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x2560), /* DE 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2562), /* DE 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2563), /* DE 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2561), /* DE 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2565), /* DE 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2566), /* DE 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2567), /* DE 2100 mPCI 3A */
+
+ IPW2100_DEV_ID(0x2570), /* GA 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x2580), /* TO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2582), /* TO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2583), /* TO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2581), /* TO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2585), /* TO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2586), /* TO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2587), /* TO 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x2590), /* SO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2592), /* SO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2591), /* SO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2593), /* SO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2596), /* SO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2598), /* SO 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x25A0), /* HP 2100 mPCI 3B */
+ {0,},
+};
+
+MODULE_DEVICE_TABLE(pci, ipw2100_pci_id_table);
+
+static struct pci_driver ipw2100_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ipw2100_pci_id_table,
+ .probe = ipw2100_pci_init_one,
+ .remove = __devexit_p(ipw2100_pci_remove_one),
+#ifdef CONFIG_PM
+ .suspend = ipw2100_suspend,
+ .resume = ipw2100_resume,
+#endif
+};
+
+
+/**
+ * Initialize the ipw2100 driver/module
+ *
+ * @returns 0 if ok, < 0 errno node con error.
+ *
+ * Note: we cannot init the /proc stuff until the PCI driver is there,
+ * or we risk an unlikely race condition on someone accessing
+ * uninitialized data in the PCI dev struct through /proc.
+ */
+static int __init ipw2100_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
+ printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT);
+
+#ifdef CONFIG_IEEE80211_NOWEP
+ IPW_DEBUG_INFO(DRV_NAME ": Compiled with WEP disabled.\n");
+#endif
+
+ ret = pci_module_init(&ipw2100_pci_driver);
+
+#ifdef CONFIG_IPW_DEBUG
+ ipw2100_debug_level = debug;
+ driver_create_file(&ipw2100_pci_driver.driver,
+ &driver_attr_debug_level);
+#endif
+
+ return ret;
+}
+
+
+/**
+ * Cleanup ipw2100 driver registration
+ */
+static void __exit ipw2100_exit(void)
+{
+ /* FIXME: IPG: check that we have no instances of the devices open */
+#ifdef CONFIG_IPW_DEBUG
+ driver_remove_file(&ipw2100_pci_driver.driver,
+ &driver_attr_debug_level);
+#endif
+ pci_unregister_driver(&ipw2100_pci_driver);
+}
+
+module_init(ipw2100_init);
+module_exit(ipw2100_exit);
+
+#define WEXT_USECHANNELS 1
+
+static const long ipw2100_frequencies[] = {
+ 2412, 2417, 2422, 2427,
+ 2432, 2437, 2442, 2447,
+ 2452, 2457, 2462, 2467,
+ 2472, 2484
+};
+
+#define FREQ_COUNT (sizeof(ipw2100_frequencies) / \
+ sizeof(ipw2100_frequencies[0]))
+
+static const long ipw2100_rates_11b[] = {
+ 1000000,
+ 2000000,
+ 5500000,
+ 11000000
+};
+
+#define RATE_COUNT (sizeof(ipw2100_rates_11b) / sizeof(ipw2100_rates_11b[0]))
+
+static int ipw2100_wx_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ if (!(priv->status & STATUS_ASSOCIATED))
+ strcpy(wrqu->name, "unassociated");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b");
+
+ IPW_DEBUG_WX("Name: %s\n", wrqu->name);
+ return 0;
+}
+
+
+static int ipw2100_wx_set_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct iw_freq *fwrq = &wrqu->freq;
+ int err = 0;
+
+ if (priv->ieee->iw_mode == IW_MODE_INFRA)
+ return -EOPNOTSUPP;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ /* if setting by freq convert to channel */
+ if (fwrq->e == 1) {
+ if ((fwrq->m >= (int) 2.412e8 &&
+ fwrq->m <= (int) 2.487e8)) {
+ int f = fwrq->m / 100000;
+ int c = 0;
+
+ while ((c < REG_MAX_CHANNEL) &&
+ (f != ipw2100_frequencies[c]))
+ c++;
+
+ /* hack to fall through */
+ fwrq->e = 0;
+ fwrq->m = c + 1;
+ }
+ }
+
+ if (fwrq->e > 0 || fwrq->m > 1000)
+ return -EOPNOTSUPP;
+ else { /* Set the channel */
+ IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
+ err = ipw2100_set_channel(priv, fwrq->m, 0);
+ }
+
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+
+static int ipw2100_wx_get_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ wrqu->freq.e = 0;
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured CHANNEL then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_CHANNEL ||
+ priv->status & STATUS_ASSOCIATED)
+ wrqu->freq.m = priv->channel;
+ else
+ wrqu->freq.m = 0;
+
+ IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
+ return 0;
+
+}
+
+static int ipw2100_wx_set_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ IPW_DEBUG_WX("SET Mode -> %d \n", wrqu->mode);
+
+ if (wrqu->mode == priv->ieee->iw_mode)
+ return 0;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ switch (wrqu->mode) {
+#ifdef CONFIG_IPW2100_MONITOR
+ case IW_MODE_MONITOR:
+ err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
+ break;
+#endif /* CONFIG_IPW2100_MONITOR */
+ case IW_MODE_ADHOC:
+ err = ipw2100_switch_mode(priv, IW_MODE_ADHOC);
+ break;
+ case IW_MODE_INFRA:
+ case IW_MODE_AUTO:
+ default:
+ err = ipw2100_switch_mode(priv, IW_MODE_INFRA);
+ break;
+ }
+
+done:
+ up(&priv->action_sem);
+ return err;
+}
+
+static int ipw2100_wx_get_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ wrqu->mode = priv->ieee->iw_mode;
+ IPW_DEBUG_WX("GET Mode -> %d\n", wrqu->mode);
+
+ return 0;
+}
+
+
+#define POWER_MODES 5
+
+/* Values are in microsecond */
+static const s32 timeout_duration[POWER_MODES] = {
+ 350000,
+ 250000,
+ 75000,
+ 37000,
+ 25000,
+};
+
+static const s32 period_duration[POWER_MODES] = {
+ 400000,
+ 700000,
+ 1000000,
+ 1000000,
+ 1000000
+};
+
+static int ipw2100_wx_get_range(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct iw_range *range = (struct iw_range *)extra;
+ u16 val;
+ int i, level;
+
+ wrqu->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ /* Let's try to keep this struct in the same order as in
+ * linux/include/wireless.h
+ */
+
+ /* TODO: See what values we can set, and remove the ones we can't
+ * set, or fill them with some default data.
+ */
+
+ /* ~5 Mb/s real (802.11b) */
+ range->throughput = 5 * 1000 * 1000;
+
+// range->sensitivity; /* signal level threshold range */
+
+ range->max_qual.qual = 100;
+ /* TODO: Find real max RSSI and stick here */
+ range->max_qual.level = 0;
+ range->max_qual.noise = 0;
+ range->max_qual.updated = 7; /* Updated all three */
+
+ range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */
+ /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+ range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM;
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = 7; /* Updated all three */
+
+ range->num_bitrates = RATE_COUNT;
+
+ for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) {
+ range->bitrate[i] = ipw2100_rates_11b[i];
+ }
+
+ range->min_rts = MIN_RTS_THRESHOLD;
+ range->max_rts = MAX_RTS_THRESHOLD;
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+
+ range->min_pmp = period_duration[0]; /* Minimal PM period */
+ range->max_pmp = period_duration[POWER_MODES-1];/* Maximal PM period */
+ range->min_pmt = timeout_duration[POWER_MODES-1]; /* Minimal PM timeout */
+ range->max_pmt = timeout_duration[0];/* Maximal PM timeout */
+
+ /* How to decode max/min PM period */
+ range->pmp_flags = IW_POWER_PERIOD;
+ /* How to decode max/min PM period */
+ range->pmt_flags = IW_POWER_TIMEOUT;
+ /* What PM options are supported */
+ range->pm_capa = IW_POWER_TIMEOUT | IW_POWER_PERIOD;
+
+ range->encoding_size[0] = 5;
+ range->encoding_size[1] = 13; /* Different token sizes */
+ range->num_encoding_sizes = 2; /* Number of entry in the list */
+ range->max_encoding_tokens = WEP_KEYS; /* Max number of tokens */
+// range->encoding_login_index; /* token index for login token */
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ range->txpower_capa = IW_TXPOW_DBM;
+ range->num_txpower = IW_MAX_TXPOWER;
+ for (i = 0, level = (IPW_TX_POWER_MAX_DBM * 16); i < IW_MAX_TXPOWER;
+ i++, level -= ((IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM) * 16) /
+ (IW_MAX_TXPOWER - 1))
+ range->txpower[i] = level / 16;
+ } else {
+ range->txpower_capa = 0;
+ range->num_txpower = 0;
+ }
+
+
+ /* Set the Wireless Extension versions */
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 16;
+
+// range->retry_capa; /* What retry options are supported */
+// range->retry_flags; /* How to decode max/min retry limit */
+// range->r_time_flags; /* How to decode max/min retry life */
+// range->min_retry; /* Minimal number of retries */
+// range->max_retry; /* Maximal number of retries */
+// range->min_r_time; /* Minimal retry lifetime */
+// range->max_r_time; /* Maximal retry lifetime */
+
+ range->num_channels = FREQ_COUNT;
+
+ val = 0;
+ for (i = 0; i < FREQ_COUNT; i++) {
+ // TODO: Include only legal frequencies for some countries
+// if (local->channel_mask & (1 << i)) {
+ range->freq[val].i = i + 1;
+ range->freq[val].m = ipw2100_frequencies[i] * 100000;
+ range->freq[val].e = 1;
+ val++;
+// }
+ if (val == IW_MAX_FREQUENCIES)
+ break;
+ }
+ range->num_frequency = val;
+
+ IPW_DEBUG_WX("GET Range\n");
+
+ return 0;
+}
+
+static int ipw2100_wx_set_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ static const unsigned char any[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+ static const unsigned char off[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ // sanity checks
+ if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
+ !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+ /* we disable mandatory BSSID association */
+ IPW_DEBUG_WX("exit - disable mandatory BSSID\n");
+ priv->config &= ~CFG_STATIC_BSSID;
+ err = ipw2100_set_mandatory_bssid(priv, NULL, 0);
+ goto done;
+ }
+
+ priv->config |= CFG_STATIC_BSSID;
+ memcpy(priv->mandatory_bssid_mac, wrqu->ap_addr.sa_data, ETH_ALEN);
+
+ err = ipw2100_set_mandatory_bssid(priv, wrqu->ap_addr.sa_data, 0);
+
+ IPW_DEBUG_WX("SET BSSID -> %02X:%02X:%02X:%02X:%02X:%02X\n",
+ wrqu->ap_addr.sa_data[0] & 0xff,
+ wrqu->ap_addr.sa_data[1] & 0xff,
+ wrqu->ap_addr.sa_data[2] & 0xff,
+ wrqu->ap_addr.sa_data[3] & 0xff,
+ wrqu->ap_addr.sa_data[4] & 0xff,
+ wrqu->ap_addr.sa_data[5] & 0xff);
+
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+static int ipw2100_wx_get_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured BSSID then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_BSSID ||
+ priv->status & STATUS_ASSOCIATED) {
+ wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(wrqu->ap_addr.sa_data, &priv->bssid, ETH_ALEN);
+ } else
+ memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+
+ IPW_DEBUG_WX("Getting WAP BSSID: " MAC_FMT "\n",
+ MAC_ARG(wrqu->ap_addr.sa_data));
+ return 0;
+}
+
+static int ipw2100_wx_set_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ char *essid = ""; /* ANY */
+ int length = 0;
+ int err = 0;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->essid.flags && wrqu->essid.length) {
+ length = wrqu->essid.length - 1;
+ essid = extra;
+ }
+
+ if (length == 0) {
+ IPW_DEBUG_WX("Setting ESSID to ANY\n");
+ priv->config &= ~CFG_STATIC_ESSID;
+ err = ipw2100_set_essid(priv, NULL, 0, 0);
+ goto done;
+ }
+
+ length = min(length, IW_ESSID_MAX_SIZE);
+
+ priv->config |= CFG_STATIC_ESSID;
+
+ if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
+ IPW_DEBUG_WX("ESSID set to current ESSID.\n");
+ err = 0;
+ goto done;
+ }
+
+ IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length),
+ length);
+
+ priv->essid_len = length;
+ memcpy(priv->essid, essid, priv->essid_len);
+
+ err = ipw2100_set_essid(priv, essid, length, 0);
+
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+static int ipw2100_wx_get_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured ESSID then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_ESSID ||
+ priv->status & STATUS_ASSOCIATED) {
+ IPW_DEBUG_WX("Getting essid: '%s'\n",
+ escape_essid(priv->essid, priv->essid_len));
+ memcpy(extra, priv->essid, priv->essid_len);
+ wrqu->essid.length = priv->essid_len;
+ wrqu->essid.flags = 1; /* active */
+ } else {
+ IPW_DEBUG_WX("Getting essid: ANY\n");
+ wrqu->essid.length = 0;
+ wrqu->essid.flags = 0; /* active */
+ }
+
+ return 0;
+}
+
+static int ipw2100_wx_set_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ if (wrqu->data.length > IW_ESSID_MAX_SIZE)
+ return -E2BIG;
+
+ wrqu->data.length = min((size_t)wrqu->data.length, sizeof(priv->nick));
+ memset(priv->nick, 0, sizeof(priv->nick));
+ memcpy(priv->nick, extra, wrqu->data.length);
+
+ IPW_DEBUG_WX("SET Nickname -> %s \n", priv->nick);
+
+ return 0;
+}
+
+static int ipw2100_wx_get_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ wrqu->data.length = strlen(priv->nick) + 1;
+ memcpy(extra, priv->nick, wrqu->data.length);
+ wrqu->data.flags = 1; /* active */
+
+ IPW_DEBUG_WX("GET Nickname -> %s \n", extra);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ u32 target_rate = wrqu->bitrate.value;
+ u32 rate;
+ int err = 0;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ rate = 0;
+
+ if (target_rate == 1000000 ||
+ (!wrqu->bitrate.fixed && target_rate > 1000000))
+ rate |= TX_RATE_1_MBIT;
+ if (target_rate == 2000000 ||
+ (!wrqu->bitrate.fixed && target_rate > 2000000))
+ rate |= TX_RATE_2_MBIT;
+ if (target_rate == 5500000 ||
+ (!wrqu->bitrate.fixed && target_rate > 5500000))
+ rate |= TX_RATE_5_5_MBIT;
+ if (target_rate == 11000000 ||
+ (!wrqu->bitrate.fixed && target_rate > 11000000))
+ rate |= TX_RATE_11_MBIT;
+ if (rate == 0)
+ rate = DEFAULT_TX_RATES;
+
+ err = ipw2100_set_tx_rates(priv, rate, 0);
+
+ IPW_DEBUG_WX("SET Rate -> %04X \n", rate);
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+
+static int ipw2100_wx_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int val;
+ int len = sizeof(val);
+ int err = 0;
+
+ if (!(priv->status & STATUS_ENABLED) ||
+ priv->status & STATUS_RF_KILL_MASK ||
+ !(priv->status & STATUS_ASSOCIATED)) {
+ wrqu->bitrate.value = 0;
+ return 0;
+ }
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ err = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &val, &len);
+ if (err) {
+ IPW_DEBUG_WX("failed querying ordinals.\n");
+ return err;
+ }
+
+ switch (val & TX_RATE_MASK) {
+ case TX_RATE_1_MBIT:
+ wrqu->bitrate.value = 1000000;
+ break;
+ case TX_RATE_2_MBIT:
+ wrqu->bitrate.value = 2000000;
+ break;
+ case TX_RATE_5_5_MBIT:
+ wrqu->bitrate.value = 5500000;
+ break;
+ case TX_RATE_11_MBIT:
+ wrqu->bitrate.value = 11000000;
+ break;
+ default:
+ wrqu->bitrate.value = 0;
+ }
+
+ IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
+
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+static int ipw2100_wx_set_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int value, err;
+
+ /* Auto RTS not yet supported */
+ if (wrqu->rts.fixed == 0)
+ return -EINVAL;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->rts.disabled)
+ value = priv->rts_threshold | RTS_DISABLED;
+ else {
+ if (wrqu->rts.value < 1 ||
+ wrqu->rts.value > 2304) {
+ err = -EINVAL;
+ goto done;
+ }
+ value = wrqu->rts.value;
+ }
+
+ err = ipw2100_set_rts_threshold(priv, value);
+
+ IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X \n", value);
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+static int ipw2100_wx_get_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ wrqu->rts.value = priv->rts_threshold & ~RTS_DISABLED;
+ wrqu->rts.fixed = 1; /* no auto select */
+
+ /* If RTS is set to the default value, then it is disabled */
+ wrqu->rts.disabled = (priv->rts_threshold & RTS_DISABLED) ? 1 : 0;
+
+ IPW_DEBUG_WX("GET RTS Threshold -> 0x%08X \n", wrqu->rts.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0, value;
+
+ if (priv->ieee->iw_mode != IW_MODE_ADHOC)
+ return -EINVAL;
+
+ if (wrqu->txpower.disabled == 1 || wrqu->txpower.fixed == 0)
+ value = IPW_TX_POWER_DEFAULT;
+ else {
+ if (wrqu->txpower.value < IPW_TX_POWER_MIN_DBM ||
+ wrqu->txpower.value > IPW_TX_POWER_MAX_DBM)
+ return -EINVAL;
+
+ value = (wrqu->txpower.value - IPW_TX_POWER_MIN_DBM) * 16 /
+ (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM);
+ }
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ err = ipw2100_set_tx_power(priv, value);
+
+ IPW_DEBUG_WX("SET TX Power -> %d \n", value);
+
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+static int ipw2100_wx_get_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ if (priv->ieee->iw_mode != IW_MODE_ADHOC) {
+ wrqu->power.disabled = 1;
+ return 0;
+ }
+
+ if (priv->tx_power == IPW_TX_POWER_DEFAULT) {
+ wrqu->power.fixed = 0;
+ wrqu->power.value = IPW_TX_POWER_MAX_DBM;
+ wrqu->power.disabled = 1;
+ } else {
+ wrqu->power.disabled = 0;
+ wrqu->power.fixed = 1;
+ wrqu->power.value =
+ (priv->tx_power *
+ (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM)) /
+ (IPW_TX_POWER_MAX - IPW_TX_POWER_MIN) +
+ IPW_TX_POWER_MIN_DBM;
+ }
+
+ wrqu->power.flags = IW_TXPOW_DBM;
+
+ IPW_DEBUG_WX("GET TX Power -> %d \n", wrqu->power.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ if (!wrqu->frag.fixed)
+ return -EINVAL;
+
+ if (wrqu->frag.disabled) {
+ priv->frag_threshold |= FRAG_DISABLED;
+ priv->ieee->fts = DEFAULT_FTS;
+ } else {
+ if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
+ wrqu->frag.value > MAX_FRAG_THRESHOLD)
+ return -EINVAL;
+
+ priv->ieee->fts = wrqu->frag.value & ~0x1;
+ priv->frag_threshold = priv->ieee->fts;
+ }
+
+ IPW_DEBUG_WX("SET Frag Threshold -> %d \n", priv->ieee->fts);
+
+ return 0;
+}
+
+static int ipw2100_wx_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ wrqu->frag.value = priv->frag_threshold & ~FRAG_DISABLED;
+ wrqu->frag.fixed = 0; /* no auto select */
+ wrqu->frag.disabled = (priv->frag_threshold & FRAG_DISABLED) ? 1 : 0;
+
+ IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ if (wrqu->retry.flags & IW_RETRY_LIFETIME ||
+ wrqu->retry.disabled)
+ return -EINVAL;
+
+ if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
+ return 0;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->retry.flags & IW_RETRY_MIN) {
+ err = ipw2100_set_short_retry(priv, wrqu->retry.value);
+ IPW_DEBUG_WX("SET Short Retry Limit -> %d \n",
+ wrqu->retry.value);
+ goto done;
+ }
+
+ if (wrqu->retry.flags & IW_RETRY_MAX) {
+ err = ipw2100_set_long_retry(priv, wrqu->retry.value);
+ IPW_DEBUG_WX("SET Long Retry Limit -> %d \n",
+ wrqu->retry.value);
+ goto done;
+ }
+
+ err = ipw2100_set_short_retry(priv, wrqu->retry.value);
+ if (!err)
+ err = ipw2100_set_long_retry(priv, wrqu->retry.value);
+
+ IPW_DEBUG_WX("SET Both Retry Limits -> %d \n", wrqu->retry.value);
+
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+static int ipw2100_wx_get_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ wrqu->retry.disabled = 0; /* can't be disabled */
+
+ if ((wrqu->retry.flags & IW_RETRY_TYPE) ==
+ IW_RETRY_LIFETIME)
+ return -EINVAL;
+
+ if (wrqu->retry.flags & IW_RETRY_MAX) {
+ wrqu->retry.flags = IW_RETRY_LIMIT & IW_RETRY_MAX;
+ wrqu->retry.value = priv->long_retry_limit;
+ } else {
+ wrqu->retry.flags =
+ (priv->short_retry_limit !=
+ priv->long_retry_limit) ?
+ IW_RETRY_LIMIT & IW_RETRY_MIN : IW_RETRY_LIMIT;
+
+ wrqu->retry.value = priv->short_retry_limit;
+ }
+
+ IPW_DEBUG_WX("GET Retry -> %d \n", wrqu->retry.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ IPW_DEBUG_WX("Initiating scan...\n");
+ if (ipw2100_set_scan_options(priv) ||
+ ipw2100_start_scan(priv)) {
+ IPW_DEBUG_WX("Start scan failed.\n");
+
+ /* TODO: Mark a scan as pending so when hardware initialized
+ * a scan starts */
+ }
+
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+static int ipw2100_wx_get_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
+}
+
+
+/*
+ * Implementation based on code in hostap-driver v0.1.3 hostap_ioctl.c
+ */
+static int ipw2100_wx_set_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ /*
+ * No check of STATUS_INITIALIZED required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw2100_wx_get_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw2100_wx_set_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->power.disabled) {
+ priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
+ err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
+ IPW_DEBUG_WX("SET Power Management Mode -> off\n");
+ goto done;
+ }
+
+ switch (wrqu->power.flags & IW_POWER_MODE) {
+ case IW_POWER_ON: /* If not specified */
+ case IW_POWER_MODE: /* If set all mask */
+ case IW_POWER_ALL_R: /* If explicitely state all */
+ break;
+ default: /* Otherwise we don't support it */
+ IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
+ wrqu->power.flags);
+ err = -EOPNOTSUPP;
+ goto done;
+ }
+
+ /* If the user hasn't specified a power management mode yet, default
+ * to BATTERY */
+ priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
+ err = ipw2100_set_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
+
+ IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n",
+ priv->power_mode);
+
+ done:
+ up(&priv->action_sem);
+ return err;
+
+}
+
+static int ipw2100_wx_get_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ if (!(priv->power_mode & IPW_POWER_ENABLED)) {
+ wrqu->power.disabled = 1;
+ } else {
+ wrqu->power.disabled = 0;
+ wrqu->power.flags = 0;
+ }
+
+ IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
+
+ return 0;
+}
+
+
+/*
+ *
+ * IWPRIV handlers
+ *
+ */
+#ifdef CONFIG_IPW2100_MONITOR
+static int ipw2100_wx_set_promisc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int *parms = (int *)extra;
+ int enable = (parms[0] > 0);
+ int err = 0;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (enable) {
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ err = ipw2100_set_channel(priv, parms[1], 0);
+ goto done;
+ }
+ priv->channel = parms[1];
+ err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
+ } else {
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+ err = ipw2100_switch_mode(priv, priv->last_mode);
+ }
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+static int ipw2100_wx_reset(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ if (priv->status & STATUS_INITIALIZED)
+ schedule_reset(priv);
+ return 0;
+}
+
+#endif
+
+static int ipw2100_wx_set_powermode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0, mode = *(int *)extra;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if ((mode < 1) || (mode > POWER_MODES))
+ mode = IPW_POWER_AUTO;
+
+ if (priv->power_mode != mode)
+ err = ipw2100_set_power_mode(priv, mode);
+ done:
+ up(&priv->action_sem);
+ return err;
+}
+
+#define MAX_POWER_STRING 80
+static int ipw2100_wx_get_powermode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int level = IPW_POWER_LEVEL(priv->power_mode);
+ s32 timeout, period;
+
+ if (!(priv->power_mode & IPW_POWER_ENABLED)) {
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d (Off)", level);
+ } else {
+ switch (level) {
+ case IPW_POWER_MODE_CAM:
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d (None)", level);
+ break;
+ case IPW_POWER_AUTO:
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d (Auto)", 0);
+ break;
+ default:
+ timeout = timeout_duration[level - 1] / 1000;
+ period = period_duration[level - 1] / 1000;
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d "
+ "(Timeout %dms, Period %dms)",
+ level, timeout, period);
+ }
+ }
+
+ wrqu->data.length = strlen(extra) + 1;
+
+ return 0;
+}
+
+
+static int ipw2100_wx_set_preamble(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err, mode = *(int *)extra;
+
+ down(&priv->action_sem);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (mode == 1)
+ priv->config |= CFG_LONG_PREAMBLE;
+ else if (mode == 0)
+ priv->config &= ~CFG_LONG_PREAMBLE;
+ else {
+ err = -EINVAL;
+ goto done;
+ }
+
+ err = ipw2100_system_config(priv, 0);
+
+done:
+ up(&priv->action_sem);
+ return err;
+}
+
+static int ipw2100_wx_get_preamble(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ if (priv->config & CFG_LONG_PREAMBLE)
+ snprintf(wrqu->name, IFNAMSIZ, "long (1)");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
+
+ return 0;
+}
+
+static iw_handler ipw2100_wx_handlers[] =
+{
+ NULL, /* SIOCSIWCOMMIT */
+ ipw2100_wx_get_name, /* SIOCGIWNAME */
+ NULL, /* SIOCSIWNWID */
+ NULL, /* SIOCGIWNWID */
+ ipw2100_wx_set_freq, /* SIOCSIWFREQ */
+ ipw2100_wx_get_freq, /* SIOCGIWFREQ */
+ ipw2100_wx_set_mode, /* SIOCSIWMODE */
+ ipw2100_wx_get_mode, /* SIOCGIWMODE */
+ NULL, /* SIOCSIWSENS */
+ NULL, /* SIOCGIWSENS */
+ NULL, /* SIOCSIWRANGE */
+ ipw2100_wx_get_range, /* SIOCGIWRANGE */
+ NULL, /* SIOCSIWPRIV */
+ NULL, /* SIOCGIWPRIV */
+ NULL, /* SIOCSIWSTATS */
+ NULL, /* SIOCGIWSTATS */
+ NULL, /* SIOCSIWSPY */
+ NULL, /* SIOCGIWSPY */
+ NULL, /* SIOCGIWTHRSPY */
+ NULL, /* SIOCWIWTHRSPY */
+ ipw2100_wx_set_wap, /* SIOCSIWAP */
+ ipw2100_wx_get_wap, /* SIOCGIWAP */
+ NULL, /* -- hole -- */
+ NULL, /* SIOCGIWAPLIST -- deprecated */
+ ipw2100_wx_set_scan, /* SIOCSIWSCAN */
+ ipw2100_wx_get_scan, /* SIOCGIWSCAN */
+ ipw2100_wx_set_essid, /* SIOCSIWESSID */
+ ipw2100_wx_get_essid, /* SIOCGIWESSID */
+ ipw2100_wx_set_nick, /* SIOCSIWNICKN */
+ ipw2100_wx_get_nick, /* SIOCGIWNICKN */
+ NULL, /* -- hole -- */
+ NULL, /* -- hole -- */
+ ipw2100_wx_set_rate, /* SIOCSIWRATE */
+ ipw2100_wx_get_rate, /* SIOCGIWRATE */
+ ipw2100_wx_set_rts, /* SIOCSIWRTS */
+ ipw2100_wx_get_rts, /* SIOCGIWRTS */
+ ipw2100_wx_set_frag, /* SIOCSIWFRAG */
+ ipw2100_wx_get_frag, /* SIOCGIWFRAG */
+ ipw2100_wx_set_txpow, /* SIOCSIWTXPOW */
+ ipw2100_wx_get_txpow, /* SIOCGIWTXPOW */
+ ipw2100_wx_set_retry, /* SIOCSIWRETRY */
+ ipw2100_wx_get_retry, /* SIOCGIWRETRY */
+ ipw2100_wx_set_encode, /* SIOCSIWENCODE */
+ ipw2100_wx_get_encode, /* SIOCGIWENCODE */
+ ipw2100_wx_set_power, /* SIOCSIWPOWER */
+ ipw2100_wx_get_power, /* SIOCGIWPOWER */
+};
+
+#define IPW2100_PRIV_SET_MONITOR SIOCIWFIRSTPRIV
+#define IPW2100_PRIV_RESET SIOCIWFIRSTPRIV+1
+#define IPW2100_PRIV_SET_POWER SIOCIWFIRSTPRIV+2
+#define IPW2100_PRIV_GET_POWER SIOCIWFIRSTPRIV+3
+#define IPW2100_PRIV_SET_LONGPREAMBLE SIOCIWFIRSTPRIV+4
+#define IPW2100_PRIV_GET_LONGPREAMBLE SIOCIWFIRSTPRIV+5
+
+static const struct iw_priv_args ipw2100_private_args[] = {
+
+#ifdef CONFIG_IPW2100_MONITOR
+ {
+ IPW2100_PRIV_SET_MONITOR,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"
+ },
+ {
+ IPW2100_PRIV_RESET,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"
+ },
+#endif /* CONFIG_IPW2100_MONITOR */
+
+ {
+ IPW2100_PRIV_SET_POWER,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_power"
+ },
+ {
+ IPW2100_PRIV_GET_POWER,
+ 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_POWER_STRING, "get_power"
+ },
+ {
+ IPW2100_PRIV_SET_LONGPREAMBLE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"
+ },
+ {
+ IPW2100_PRIV_GET_LONGPREAMBLE,
+ 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_preamble"
+ },
+};
+
+static iw_handler ipw2100_private_handler[] = {
+#ifdef CONFIG_IPW2100_MONITOR
+ ipw2100_wx_set_promisc,
+ ipw2100_wx_reset,
+#else /* CONFIG_IPW2100_MONITOR */
+ NULL,
+ NULL,
+#endif /* CONFIG_IPW2100_MONITOR */
+ ipw2100_wx_set_powermode,
+ ipw2100_wx_get_powermode,
+ ipw2100_wx_set_preamble,
+ ipw2100_wx_get_preamble,
+};
+
+static struct iw_handler_def ipw2100_wx_handler_def =
+{
+ .standard = ipw2100_wx_handlers,
+ .num_standard = sizeof(ipw2100_wx_handlers) / sizeof(iw_handler),
+ .num_private = sizeof(ipw2100_private_handler) / sizeof(iw_handler),
+ .num_private_args = sizeof(ipw2100_private_args) /
+ sizeof(struct iw_priv_args),
+ .private = (iw_handler *)ipw2100_private_handler,
+ .private_args = (struct iw_priv_args *)ipw2100_private_args,
+};
+
+/*
+ * Get wireless statistics.
+ * Called by /proc/net/wireless
+ * Also called by SIOCGIWSTATS
+ */
+static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device * dev)
+{
+ enum {
+ POOR = 30,
+ FAIR = 60,
+ GOOD = 80,
+ VERY_GOOD = 90,
+ EXCELLENT = 95,
+ PERFECT = 100
+ };
+ int rssi_qual;
+ int tx_qual;
+ int beacon_qual;
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct iw_statistics *wstats;
+ u32 rssi, quality, tx_retries, missed_beacons, tx_failures;
+ u32 ord_len = sizeof(u32);
+
+ if (!priv)
+ return (struct iw_statistics *) NULL;
+
+ wstats = &priv->wstats;
+
+ /* if hw is disabled, then ipw2100_get_ordinal() can't be called.
+ * ipw2100_wx_wireless_stats seems to be called before fw is
+ * initialized. STATUS_ASSOCIATED will only be set if the hw is up
+ * and associated; if not associcated, the values are all meaningless
+ * anyway, so set them all to NULL and INVALID */
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ wstats->miss.beacon = 0;
+ wstats->discard.retries = 0;
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ wstats->qual.noise = 0;
+ wstats->qual.updated = 7;
+ wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
+ IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+ return wstats;
+ }
+
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_MISSED_BCNS,
+ &missed_beacons, &ord_len))
+ goto fail_get_ordinal;
+
+ /* If we don't have a connection the quality and level is 0*/
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ } else {
+ if (ipw2100_get_ordinal(priv, IPW_ORD_RSSI_AVG_CURR,
+ &rssi, &ord_len))
+ goto fail_get_ordinal;
+ wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
+ if (rssi < 10)
+ rssi_qual = rssi * POOR / 10;
+ else if (rssi < 15)
+ rssi_qual = (rssi - 10) * (FAIR - POOR) / 5 + POOR;
+ else if (rssi < 20)
+ rssi_qual = (rssi - 15) * (GOOD - FAIR) / 5 + FAIR;
+ else if (rssi < 30)
+ rssi_qual = (rssi - 20) * (VERY_GOOD - GOOD) /
+ 10 + GOOD;
+ else
+ rssi_qual = (rssi - 30) * (PERFECT - VERY_GOOD) /
+ 10 + VERY_GOOD;
+
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_RETRIES,
+ &tx_retries, &ord_len))
+ goto fail_get_ordinal;
+
+ if (tx_retries > 75)
+ tx_qual = (90 - tx_retries) * POOR / 15;
+ else if (tx_retries > 70)
+ tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
+ else if (tx_retries > 65)
+ tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
+ else if (tx_retries > 50)
+ tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
+ 15 + GOOD;
+ else
+ tx_qual = (50 - tx_retries) *
+ (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
+
+ if (missed_beacons > 50)
+ beacon_qual = (60 - missed_beacons) * POOR / 10;
+ else if (missed_beacons > 40)
+ beacon_qual = (50 - missed_beacons) * (FAIR - POOR) /
+ 10 + POOR;
+ else if (missed_beacons > 32)
+ beacon_qual = (40 - missed_beacons) * (GOOD - FAIR) /
+ 18 + FAIR;
+ else if (missed_beacons > 20)
+ beacon_qual = (32 - missed_beacons) *
+ (VERY_GOOD - GOOD) / 20 + GOOD;
+ else
+ beacon_qual = (20 - missed_beacons) *
+ (PERFECT - VERY_GOOD) / 20 + VERY_GOOD;
+
+ quality = min(beacon_qual, min(tx_qual, rssi_qual));
+
+#ifdef CONFIG_IPW_DEBUG
+ if (beacon_qual == quality)
+ IPW_DEBUG_WX("Quality clamped by Missed Beacons\n");
+ else if (tx_qual == quality)
+ IPW_DEBUG_WX("Quality clamped by Tx Retries\n");
+ else if (quality != 100)
+ IPW_DEBUG_WX("Quality clamped by Signal Strength\n");
+ else
+ IPW_DEBUG_WX("Quality not clamped.\n");
+#endif
+
+ wstats->qual.qual = quality;
+ wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
+ }
+
+ wstats->qual.noise = 0;
+ wstats->qual.updated = 7;
+ wstats->qual.updated |= IW_QUAL_NOISE_INVALID;
+
+ /* FIXME: this is percent and not a # */
+ wstats->miss.beacon = missed_beacons;
+
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURES,
+ &tx_failures, &ord_len))
+ goto fail_get_ordinal;
+ wstats->discard.retries = tx_failures;
+
+ return wstats;
+
+ fail_get_ordinal:
+ IPW_DEBUG_WX("failed querying ordinals.\n");
+
+ return (struct iw_statistics *) NULL;
+}
+
+static void ipw2100_wx_event_work(struct ipw2100_priv *priv)
+{
+ union iwreq_data wrqu;
+ int len = ETH_ALEN;
+
+ if (priv->status & STATUS_STOPPING)
+ return;
+
+ down(&priv->action_sem);
+
+ IPW_DEBUG_WX("enter\n");
+
+ up(&priv->action_sem);
+
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+ /* Fetch BSSID from the hardware */
+ if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) ||
+ priv->status & STATUS_RF_KILL_MASK ||
+ ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
+ &priv->bssid, &len)) {
+ memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+ } else {
+ /* We now have the BSSID, so can finish setting to the full
+ * associated state */
+ memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
+ memcpy(&priv->ieee->bssid, priv->bssid, ETH_ALEN);
+ priv->status &= ~STATUS_ASSOCIATING;
+ priv->status |= STATUS_ASSOCIATED;
+ netif_carrier_on(priv->net_dev);
+ if (netif_queue_stopped(priv->net_dev)) {
+ IPW_DEBUG_INFO("Waking net queue.\n");
+ netif_wake_queue(priv->net_dev);
+ } else {
+ IPW_DEBUG_INFO("Starting net queue.\n");
+ netif_start_queue(priv->net_dev);
+ }
+ }
+
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ IPW_DEBUG_WX("Configuring ESSID\n");
+ down(&priv->action_sem);
+ /* This is a disassociation event, so kick the firmware to
+ * look for another AP */
+ if (priv->config & CFG_STATIC_ESSID)
+ ipw2100_set_essid(priv, priv->essid, priv->essid_len, 0);
+ else
+ ipw2100_set_essid(priv, NULL, 0, 0);
+ up(&priv->action_sem);
+ }
+
+ wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+#define IPW2100_FW_MAJOR_VERSION 1
+#define IPW2100_FW_MINOR_VERSION 3
+
+#define IPW2100_FW_MINOR(x) ((x & 0xff) >> 8)
+#define IPW2100_FW_MAJOR(x) (x & 0xff)
+
+#define IPW2100_FW_VERSION ((IPW2100_FW_MINOR_VERSION << 8) | \
+ IPW2100_FW_MAJOR_VERSION)
+
+#define IPW2100_FW_PREFIX "ipw2100-" __stringify(IPW2100_FW_MAJOR_VERSION) \
+"." __stringify(IPW2100_FW_MINOR_VERSION)
+
+#define IPW2100_FW_NAME(x) IPW2100_FW_PREFIX "" x ".fw"
+
+
+/*
+
+BINARY FIRMWARE HEADER FORMAT
+
+offset length desc
+0 2 version
+2 2 mode == 0:BSS,1:IBSS,2:MONITOR
+4 4 fw_len
+8 4 uc_len
+C fw_len firmware data
+12 + fw_len uc_len microcode data
+
+*/
+
+struct ipw2100_fw_header {
+ short version;
+ short mode;
+ unsigned int fw_size;
+ unsigned int uc_size;
+} __attribute__ ((packed));
+
+
+
+static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw)
+{
+ struct ipw2100_fw_header *h =
+ (struct ipw2100_fw_header *)fw->fw_entry->data;
+
+ if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
+ printk(KERN_WARNING DRV_NAME ": Firmware image not compatible "
+ "(detected version id of %u). "
+ "See Documentation/networking/README.ipw2100\n",
+ h->version);
+ return 1;
+ }
+
+ fw->version = h->version;
+ fw->fw.data = fw->fw_entry->data + sizeof(struct ipw2100_fw_header);
+ fw->fw.size = h->fw_size;
+ fw->uc.data = fw->fw.data + h->fw_size;
+ fw->uc.size = h->uc_size;
+
+ return 0;
+}
+
+
+static int ipw2100_get_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw)
+{
+ char *fw_name;
+ int rc;
+
+ IPW_DEBUG_INFO("%s: Using hotplug firmware load.\n",
+ priv->net_dev->name);
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ fw_name = IPW2100_FW_NAME("-i");
+ break;
+#ifdef CONFIG_IPW2100_MONITOR
+ case IW_MODE_MONITOR:
+ fw_name = IPW2100_FW_NAME("-p");
+ break;
+#endif
+ case IW_MODE_INFRA:
+ default:
+ fw_name = IPW2100_FW_NAME("");
+ break;
+ }
+
+ rc = request_firmware(&fw->fw_entry, fw_name, &priv->pci_dev->dev);
+
+ if (rc < 0) {
+ printk(KERN_ERR DRV_NAME ": "
+ "%s: Firmware '%s' not available or load failed.\n",
+ priv->net_dev->name, fw_name);
+ return rc;
+ }
+ IPW_DEBUG_INFO("firmware data %p size %zd\n", fw->fw_entry->data,
+ fw->fw_entry->size);
+
+ ipw2100_mod_firmware_load(fw);
+
+ return 0;
+}
+
+static void ipw2100_release_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw)
+{
+ fw->version = 0;
+ if (fw->fw_entry)
+ release_firmware(fw->fw_entry);
+ fw->fw_entry = NULL;
+}
+
+
+static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
+ size_t max)
+{
+ char ver[MAX_FW_VERSION_LEN];
+ u32 len = MAX_FW_VERSION_LEN;
+ u32 tmp;
+ int i;
+ /* firmware version is an ascii string (max len of 14) */
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_FW_VER_NUM,
+ ver, &len))
+ return -EIO;
+ tmp = max;
+ if (len >= max)
+ len = max - 1;
+ for (i = 0; i < len; i++)
+ buf[i] = ver[i];
+ buf[i] = '\0';
+ return tmp;
+}
+
+static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
+ size_t max)
+{
+ u32 ver;
+ u32 len = sizeof(ver);
+ /* microcode version is a 32 bit integer */
+ if (ipw2100_get_ordinal(priv, IPW_ORD_UCODE_VERSION,
+ &ver, &len))
+ return -EIO;
+ return snprintf(buf, max, "%08X", ver);
+}
+
+/*
+ * On exit, the firmware will have been freed from the fw list
+ */
+static int ipw2100_fw_download(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw)
+{
+ /* firmware is constructed of N contiguous entries, each entry is
+ * structured as:
+ *
+ * offset sie desc
+ * 0 4 address to write to
+ * 4 2 length of data run
+ * 6 length data
+ */
+ unsigned int addr;
+ unsigned short len;
+
+ const unsigned char *firmware_data = fw->fw.data;
+ unsigned int firmware_data_left = fw->fw.size;
+
+ while (firmware_data_left > 0) {
+ addr = *(u32 *)(firmware_data);
+ firmware_data += 4;
+ firmware_data_left -= 4;
+
+ len = *(u16 *)(firmware_data);
+ firmware_data += 2;
+ firmware_data_left -= 2;
+
+ if (len > 32) {
+ printk(KERN_ERR DRV_NAME ": "
+ "Invalid firmware run-length of %d bytes\n",
+ len);
+ return -EINVAL;
+ }
+
+ write_nic_memory(priv->net_dev, addr, len, firmware_data);
+ firmware_data += len;
+ firmware_data_left -= len;
+ }
+
+ return 0;
+}
+
+struct symbol_alive_response {
+ u8 cmd_id;
+ u8 seq_num;
+ u8 ucode_rev;
+ u8 eeprom_valid;
+ u16 valid_flags;
+ u8 IEEE_addr[6];
+ u16 flags;
+ u16 pcb_rev;
+ u16 clock_settle_time; // 1us LSB
+ u16 powerup_settle_time; // 1us LSB
+ u16 hop_settle_time; // 1us LSB
+ u8 date[3]; // month, day, year
+ u8 time[2]; // hours, minutes
+ u8 ucode_valid;
+};
+
+static int ipw2100_ucode_download(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw)
+{
+ struct net_device *dev = priv->net_dev;
+ const unsigned char *microcode_data = fw->uc.data;
+ unsigned int microcode_data_left = fw->uc.size;
+
+ struct symbol_alive_response response;
+ int i, j;
+ u8 data;
+
+ /* Symbol control */
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
+ readl((void *)(dev->base_addr));
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
+ readl((void *)(dev->base_addr));
+
+ /* HW config */
+ write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */
+ readl((void *)(dev->base_addr));
+ write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */
+ readl((void *)(dev->base_addr));
+
+ /* EN_CS_ACCESS bit to reset control store pointer */
+ write_nic_byte(dev, 0x210000, 0x40);
+ readl((void *)(dev->base_addr));
+ write_nic_byte(dev, 0x210000, 0x0);
+ readl((void *)(dev->base_addr));
+ write_nic_byte(dev, 0x210000, 0x40);
+ readl((void *)(dev->base_addr));
+
+ /* copy microcode from buffer into Symbol */
+
+ while (microcode_data_left > 0) {
+ write_nic_byte(dev, 0x210010, *microcode_data++);
+ write_nic_byte(dev, 0x210010, *microcode_data++);
+ microcode_data_left -= 2;
+ }
+
+ /* EN_CS_ACCESS bit to reset the control store pointer */
+ write_nic_byte(dev, 0x210000, 0x0);
+ readl((void *)(dev->base_addr));
+
+ /* Enable System (Reg 0)
+ * first enable causes garbage in RX FIFO */
+ write_nic_byte(dev, 0x210000, 0x0);
+ readl((void *)(dev->base_addr));
+ write_nic_byte(dev, 0x210000, 0x80);
+ readl((void *)(dev->base_addr));
+
+ /* Reset External Baseband Reg */
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
+ readl((void *)(dev->base_addr));
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
+ readl((void *)(dev->base_addr));
+
+ /* HW Config (Reg 5) */
+ write_nic_byte(dev, 0x210014, 0x72); // fifo width =16
+ readl((void *)(dev->base_addr));
+ write_nic_byte(dev, 0x210014, 0x72); // fifo width =16
+ readl((void *)(dev->base_addr));
+
+ /* Enable System (Reg 0)
+ * second enable should be OK */
+ write_nic_byte(dev, 0x210000, 0x00); // clear enable system
+ readl((void *)(dev->base_addr));
+ write_nic_byte(dev, 0x210000, 0x80); // set enable system
+
+ /* check Symbol is enabled - upped this from 5 as it wasn't always
+ * catching the update */
+ for (i = 0; i < 10; i++) {
+ udelay(10);
+
+ /* check Dino is enabled bit */
+ read_nic_byte(dev, 0x210000, &data);
+ if (data & 0x1)
+ break;
+ }
+
+ if (i == 10) {
+ printk(KERN_ERR DRV_NAME ": %s: Error initializing Symbol\n",
+ dev->name);
+ return -EIO;
+ }
+
+ /* Get Symbol alive response */
+ for (i = 0; i < 30; i++) {
+ /* Read alive response structure */
+ for (j = 0;
+ j < (sizeof(struct symbol_alive_response) >> 1);
+ j++)
+ read_nic_word(dev, 0x210004,
+ ((u16 *)&response) + j);
+
+ if ((response.cmd_id == 1) &&
+ (response.ucode_valid == 0x1))
+ break;
+ udelay(10);
+ }
+
+ if (i == 30) {
+ printk(KERN_ERR DRV_NAME ": %s: No response from Symbol - hw not alive\n",
+ dev->name);
+ printk_buf(IPW_DL_ERROR, (u8*)&response, sizeof(response));
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ipw2100.h b/drivers/net/wireless/ipw2100.h
new file mode 100644
index 000000000000..2a3cdbd50168
--- /dev/null
+++ b/drivers/net/wireless/ipw2100.h
@@ -0,0 +1,1167 @@
+/******************************************************************************
+
+ Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************/
+#ifndef _IPW2100_H
+#define _IPW2100_H
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <asm/io.h>
+#include <linux/socket.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/version.h>
+#include <net/iw_handler.h> // new driver API
+
+#include <net/ieee80211.h>
+
+#include <linux/workqueue.h>
+
+struct ipw2100_priv;
+struct ipw2100_tx_packet;
+struct ipw2100_rx_packet;
+
+#define IPW_DL_UNINIT 0x80000000
+#define IPW_DL_NONE 0x00000000
+#define IPW_DL_ALL 0x7FFFFFFF
+
+/*
+ * To use the debug system;
+ *
+ * If you are defining a new debug classification, simply add it to the #define
+ * list here in the form of:
+ *
+ * #define IPW_DL_xxxx VALUE
+ *
+ * shifting value to the left one bit from the previous entry. xxxx should be
+ * the name of the classification (for example, WEP)
+ *
+ * You then need to either add a IPW2100_xxxx_DEBUG() macro definition for your
+ * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want
+ * to send output to that classification.
+ *
+ * To add your debug level to the list of levels seen when you perform
+ *
+ * % cat /proc/net/ipw2100/debug_level
+ *
+ * you simply need to add your entry to the ipw2100_debug_levels array.
+ *
+ * If you do not see debug_level in /proc/net/ipw2100 then you do not have
+ * CONFIG_IPW_DEBUG defined in your kernel configuration
+ *
+ */
+
+#define IPW_DL_ERROR (1<<0)
+#define IPW_DL_WARNING (1<<1)
+#define IPW_DL_INFO (1<<2)
+#define IPW_DL_WX (1<<3)
+#define IPW_DL_HC (1<<5)
+#define IPW_DL_STATE (1<<6)
+
+#define IPW_DL_NOTIF (1<<10)
+#define IPW_DL_SCAN (1<<11)
+#define IPW_DL_ASSOC (1<<12)
+#define IPW_DL_DROP (1<<13)
+
+#define IPW_DL_IOCTL (1<<14)
+#define IPW_DL_RF_KILL (1<<17)
+
+
+#define IPW_DL_MANAGE (1<<15)
+#define IPW_DL_FW (1<<16)
+
+#define IPW_DL_FRAG (1<<21)
+#define IPW_DL_WEP (1<<22)
+#define IPW_DL_TX (1<<23)
+#define IPW_DL_RX (1<<24)
+#define IPW_DL_ISR (1<<25)
+#define IPW_DL_IO (1<<26)
+#define IPW_DL_TRACE (1<<28)
+
+#define IPW_DEBUG_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a)
+#define IPW_DEBUG_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a)
+#define IPW_DEBUG_INFO(f...) IPW_DEBUG(IPW_DL_INFO, ## f)
+#define IPW_DEBUG_WX(f...) IPW_DEBUG(IPW_DL_WX, ## f)
+#define IPW_DEBUG_SCAN(f...) IPW_DEBUG(IPW_DL_SCAN, ## f)
+#define IPW_DEBUG_NOTIF(f...) IPW_DEBUG(IPW_DL_NOTIF, ## f)
+#define IPW_DEBUG_TRACE(f...) IPW_DEBUG(IPW_DL_TRACE, ## f)
+#define IPW_DEBUG_RX(f...) IPW_DEBUG(IPW_DL_RX, ## f)
+#define IPW_DEBUG_TX(f...) IPW_DEBUG(IPW_DL_TX, ## f)
+#define IPW_DEBUG_ISR(f...) IPW_DEBUG(IPW_DL_ISR, ## f)
+#define IPW_DEBUG_MANAGEMENT(f...) IPW_DEBUG(IPW_DL_MANAGE, ## f)
+#define IPW_DEBUG_WEP(f...) IPW_DEBUG(IPW_DL_WEP, ## f)
+#define IPW_DEBUG_HC(f...) IPW_DEBUG(IPW_DL_HC, ## f)
+#define IPW_DEBUG_FRAG(f...) IPW_DEBUG(IPW_DL_FRAG, ## f)
+#define IPW_DEBUG_FW(f...) IPW_DEBUG(IPW_DL_FW, ## f)
+#define IPW_DEBUG_RF_KILL(f...) IPW_DEBUG(IPW_DL_RF_KILL, ## f)
+#define IPW_DEBUG_DROP(f...) IPW_DEBUG(IPW_DL_DROP, ## f)
+#define IPW_DEBUG_IO(f...) IPW_DEBUG(IPW_DL_IO, ## f)
+#define IPW_DEBUG_IOCTL(f...) IPW_DEBUG(IPW_DL_IOCTL, ## f)
+#define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
+#define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
+
+enum {
+ IPW_HW_STATE_DISABLED = 1,
+ IPW_HW_STATE_ENABLED = 0
+};
+
+struct ssid_context {
+ char ssid[IW_ESSID_MAX_SIZE + 1];
+ int ssid_len;
+ unsigned char bssid[ETH_ALEN];
+ int port_type;
+ int channel;
+
+};
+
+extern const char *port_type_str[];
+extern const char *band_str[];
+
+#define NUMBER_OF_BD_PER_COMMAND_PACKET 1
+#define NUMBER_OF_BD_PER_DATA_PACKET 2
+
+#define IPW_MAX_BDS 6
+#define NUMBER_OF_OVERHEAD_BDS_PER_PACKETR 2
+#define NUMBER_OF_BDS_TO_LEAVE_FOR_COMMANDS 1
+
+#define REQUIRED_SPACE_IN_RING_FOR_COMMAND_PACKET \
+ (IPW_BD_QUEUE_W_R_MIN_SPARE + NUMBER_OF_BD_PER_COMMAND_PACKET)
+
+struct bd_status {
+ union {
+ struct { u8 nlf:1, txType:2, intEnabled:1, reserved:4;} fields;
+ u8 field;
+ } info;
+} __attribute__ ((packed));
+
+struct ipw2100_bd {
+ u32 host_addr;
+ u32 buf_length;
+ struct bd_status status;
+ /* number of fragments for frame (should be set only for
+ * 1st TBD) */
+ u8 num_fragments;
+ u8 reserved[6];
+} __attribute__ ((packed));
+
+#define IPW_BD_QUEUE_LENGTH(n) (1<<n)
+#define IPW_BD_ALIGNMENT(L) (L*sizeof(struct ipw2100_bd))
+
+#define IPW_BD_STATUS_TX_FRAME_802_3 0x00
+#define IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT 0x01
+#define IPW_BD_STATUS_TX_FRAME_COMMAND 0x02
+#define IPW_BD_STATUS_TX_FRAME_802_11 0x04
+#define IPW_BD_STATUS_TX_INTERRUPT_ENABLE 0x08
+
+struct ipw2100_bd_queue {
+ /* driver (virtual) pointer to queue */
+ struct ipw2100_bd *drv;
+
+ /* firmware (physical) pointer to queue */
+ dma_addr_t nic;
+
+ /* Length of phy memory allocated for BDs */
+ u32 size;
+
+ /* Number of BDs in queue (and in array) */
+ u32 entries;
+
+ /* Number of available BDs (invalid for NIC BDs) */
+ u32 available;
+
+ /* Offset of oldest used BD in array (next one to
+ * check for completion) */
+ u32 oldest;
+
+ /* Offset of next available (unused) BD */
+ u32 next;
+};
+
+#define RX_QUEUE_LENGTH 256
+#define TX_QUEUE_LENGTH 256
+#define HW_QUEUE_LENGTH 256
+
+#define TX_PENDED_QUEUE_LENGTH (TX_QUEUE_LENGTH / NUMBER_OF_BD_PER_DATA_PACKET)
+
+#define STATUS_TYPE_MASK 0x0000000f
+#define COMMAND_STATUS_VAL 0
+#define STATUS_CHANGE_VAL 1
+#define P80211_DATA_VAL 2
+#define P8023_DATA_VAL 3
+#define HOST_NOTIFICATION_VAL 4
+
+#define IPW2100_RSSI_TO_DBM (-98)
+
+struct ipw2100_status {
+ u32 frame_size;
+ u16 status_fields;
+ u8 flags;
+#define IPW_STATUS_FLAG_DECRYPTED (1<<0)
+#define IPW_STATUS_FLAG_WEP_ENCRYPTED (1<<1)
+#define IPW_STATUS_FLAG_CRC_ERROR (1<<2)
+ u8 rssi;
+} __attribute__ ((packed));
+
+struct ipw2100_status_queue {
+ /* driver (virtual) pointer to queue */
+ struct ipw2100_status *drv;
+
+ /* firmware (physical) pointer to queue */
+ dma_addr_t nic;
+
+ /* Length of phy memory allocated for BDs */
+ u32 size;
+};
+
+#define HOST_COMMAND_PARAMS_REG_LEN 100
+#define CMD_STATUS_PARAMS_REG_LEN 3
+
+#define IPW_WPA_CAPABILITIES 0x1
+#define IPW_WPA_LISTENINTERVAL 0x2
+#define IPW_WPA_AP_ADDRESS 0x4
+
+#define IPW_MAX_VAR_IE_LEN ((HOST_COMMAND_PARAMS_REG_LEN - 4) * sizeof(u32))
+
+struct ipw2100_wpa_assoc_frame {
+ u16 fixed_ie_mask;
+ struct {
+ u16 capab_info;
+ u16 listen_interval;
+ u8 current_ap[ETH_ALEN];
+ } fixed_ies;
+ u32 var_ie_len;
+ u8 var_ie[IPW_MAX_VAR_IE_LEN];
+};
+
+#define IPW_BSS 1
+#define IPW_MONITOR 2
+#define IPW_IBSS 3
+
+/**
+ * @struct _tx_cmd - HWCommand
+ * @brief H/W command structure.
+ */
+struct ipw2100_cmd_header {
+ u32 host_command_reg;
+ u32 host_command_reg1;
+ u32 sequence;
+ u32 host_command_len_reg;
+ u32 host_command_params_reg[HOST_COMMAND_PARAMS_REG_LEN];
+ u32 cmd_status_reg;
+ u32 cmd_status_params_reg[CMD_STATUS_PARAMS_REG_LEN];
+ u32 rxq_base_ptr;
+ u32 rxq_next_ptr;
+ u32 rxq_host_ptr;
+ u32 txq_base_ptr;
+ u32 txq_next_ptr;
+ u32 txq_host_ptr;
+ u32 tx_status_reg;
+ u32 reserved;
+ u32 status_change_reg;
+ u32 reserved1[3];
+ u32 *ordinal1_ptr;
+ u32 *ordinal2_ptr;
+} __attribute__ ((packed));
+
+struct ipw2100_data_header {
+ u32 host_command_reg;
+ u32 host_command_reg1;
+ u8 encrypted; // BOOLEAN in win! TRUE if frame is enc by driver
+ u8 needs_encryption; // BOOLEAN in win! TRUE if frma need to be enc in NIC
+ u8 wep_index; // 0 no key, 1-4 key index, 0xff immediate key
+ u8 key_size; // 0 no imm key, 0x5 64bit encr, 0xd 128bit encr, 0x10 128bit encr and 128bit IV
+ u8 key[16];
+ u8 reserved[10]; // f/w reserved
+ u8 src_addr[ETH_ALEN];
+ u8 dst_addr[ETH_ALEN];
+ u16 fragment_size;
+} __attribute__ ((packed));
+
+/* Host command data structure */
+struct host_command {
+ u32 host_command; // COMMAND ID
+ u32 host_command1; // COMMAND ID
+ u32 host_command_sequence; // UNIQUE COMMAND NUMBER (ID)
+ u32 host_command_length; // LENGTH
+ u32 host_command_parameters[HOST_COMMAND_PARAMS_REG_LEN]; // COMMAND PARAMETERS
+} __attribute__ ((packed));
+
+
+typedef enum {
+ POWER_ON_RESET,
+ EXIT_POWER_DOWN_RESET,
+ SW_RESET,
+ EEPROM_RW,
+ SW_RE_INIT
+} ipw2100_reset_event;
+
+enum {
+ COMMAND = 0xCAFE,
+ DATA,
+ RX
+};
+
+
+struct ipw2100_tx_packet {
+ int type;
+ int index;
+ union {
+ struct { /* COMMAND */
+ struct ipw2100_cmd_header* cmd;
+ dma_addr_t cmd_phys;
+ } c_struct;
+ struct { /* DATA */
+ struct ipw2100_data_header* data;
+ dma_addr_t data_phys;
+ struct ieee80211_txb *txb;
+ } d_struct;
+ } info;
+ int jiffy_start;
+
+ struct list_head list;
+};
+
+
+struct ipw2100_rx_packet {
+ struct ipw2100_rx *rxp;
+ dma_addr_t dma_addr;
+ int jiffy_start;
+ struct sk_buff *skb;
+ struct list_head list;
+};
+
+#define FRAG_DISABLED (1<<31)
+#define RTS_DISABLED (1<<31)
+#define MAX_RTS_THRESHOLD 2304U
+#define MIN_RTS_THRESHOLD 1U
+#define DEFAULT_RTS_THRESHOLD 1000U
+
+#define DEFAULT_BEACON_INTERVAL 100U
+#define DEFAULT_SHORT_RETRY_LIMIT 7U
+#define DEFAULT_LONG_RETRY_LIMIT 4U
+
+struct ipw2100_ordinals {
+ u32 table1_addr;
+ u32 table2_addr;
+ u32 table1_size;
+ u32 table2_size;
+};
+
+/* Host Notification header */
+struct ipw2100_notification {
+ u32 hnhdr_subtype; /* type of host notification */
+ u32 hnhdr_size; /* size in bytes of data
+ or number of entries, if table.
+ Does NOT include header */
+} __attribute__ ((packed));
+
+#define MAX_KEY_SIZE 16
+#define MAX_KEYS 8
+
+#define IPW2100_WEP_ENABLE (1<<1)
+#define IPW2100_WEP_DROP_CLEAR (1<<2)
+
+#define IPW_NONE_CIPHER (1<<0)
+#define IPW_WEP40_CIPHER (1<<1)
+#define IPW_TKIP_CIPHER (1<<2)
+#define IPW_CCMP_CIPHER (1<<4)
+#define IPW_WEP104_CIPHER (1<<5)
+#define IPW_CKIP_CIPHER (1<<6)
+
+#define IPW_AUTH_OPEN 0
+#define IPW_AUTH_SHARED 1
+
+struct statistic {
+ int value;
+ int hi;
+ int lo;
+};
+
+#define INIT_STAT(x) do { \
+ (x)->value = (x)->hi = 0; \
+ (x)->lo = 0x7fffffff; \
+} while (0)
+#define SET_STAT(x,y) do { \
+ (x)->value = y; \
+ if ((x)->value > (x)->hi) (x)->hi = (x)->value; \
+ if ((x)->value < (x)->lo) (x)->lo = (x)->value; \
+} while (0)
+#define INC_STAT(x) do { if (++(x)->value > (x)->hi) (x)->hi = (x)->value; } \
+while (0)
+#define DEC_STAT(x) do { if (--(x)->value < (x)->lo) (x)->lo = (x)->value; } \
+while (0)
+
+#define IPW2100_ERROR_QUEUE 5
+
+/* Power management code: enable or disable? */
+enum {
+#ifdef CONFIG_PM
+ IPW2100_PM_DISABLED = 0,
+ PM_STATE_SIZE = 16,
+#else
+ IPW2100_PM_DISABLED = 1,
+ PM_STATE_SIZE = 0,
+#endif
+};
+
+#define STATUS_POWERED (1<<0)
+#define STATUS_CMD_ACTIVE (1<<1) /**< host command in progress */
+#define STATUS_RUNNING (1<<2) /* Card initialized, but not enabled */
+#define STATUS_ENABLED (1<<3) /* Card enabled -- can scan,Tx,Rx */
+#define STATUS_STOPPING (1<<4) /* Card is in shutdown phase */
+#define STATUS_INITIALIZED (1<<5) /* Card is ready for external calls */
+#define STATUS_ASSOCIATING (1<<9) /* Associated, but no BSSID yet */
+#define STATUS_ASSOCIATED (1<<10) /* Associated and BSSID valid */
+#define STATUS_INT_ENABLED (1<<11)
+#define STATUS_RF_KILL_HW (1<<12)
+#define STATUS_RF_KILL_SW (1<<13)
+#define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW)
+#define STATUS_EXIT_PENDING (1<<14)
+
+#define STATUS_SCAN_PENDING (1<<23)
+#define STATUS_SCANNING (1<<24)
+#define STATUS_SCAN_ABORTING (1<<25)
+#define STATUS_SCAN_COMPLETE (1<<26)
+#define STATUS_WX_EVENT_PENDING (1<<27)
+#define STATUS_RESET_PENDING (1<<29)
+#define STATUS_SECURITY_UPDATED (1<<30) /* Security sync needed */
+
+
+
+/* Internal NIC states */
+#define IPW_STATE_INITIALIZED (1<<0)
+#define IPW_STATE_COUNTRY_FOUND (1<<1)
+#define IPW_STATE_ASSOCIATED (1<<2)
+#define IPW_STATE_ASSN_LOST (1<<3)
+#define IPW_STATE_ASSN_CHANGED (1<<4)
+#define IPW_STATE_SCAN_COMPLETE (1<<5)
+#define IPW_STATE_ENTERED_PSP (1<<6)
+#define IPW_STATE_LEFT_PSP (1<<7)
+#define IPW_STATE_RF_KILL (1<<8)
+#define IPW_STATE_DISABLED (1<<9)
+#define IPW_STATE_POWER_DOWN (1<<10)
+#define IPW_STATE_SCANNING (1<<11)
+
+
+
+#define CFG_STATIC_CHANNEL (1<<0) /* Restrict assoc. to single channel */
+#define CFG_STATIC_ESSID (1<<1) /* Restrict assoc. to single SSID */
+#define CFG_STATIC_BSSID (1<<2) /* Restrict assoc. to single BSSID */
+#define CFG_CUSTOM_MAC (1<<3)
+#define CFG_LONG_PREAMBLE (1<<4)
+#define CFG_ASSOCIATE (1<<6)
+#define CFG_FIXED_RATE (1<<7)
+#define CFG_ADHOC_CREATE (1<<8)
+#define CFG_C3_DISABLED (1<<9)
+#define CFG_PASSIVE_SCAN (1<<10)
+
+#define CAP_SHARED_KEY (1<<0) /* Off = OPEN */
+#define CAP_PRIVACY_ON (1<<1) /* Off = No privacy */
+
+struct ipw2100_priv {
+
+ int stop_hang_check; /* Set 1 when shutting down to kill hang_check */
+ int stop_rf_kill; /* Set 1 when shutting down to kill rf_kill */
+
+ struct ieee80211_device *ieee;
+ unsigned long status;
+ unsigned long config;
+ unsigned long capability;
+
+ /* Statistics */
+ int resets;
+ int reset_backoff;
+
+ /* Context */
+ u8 essid[IW_ESSID_MAX_SIZE];
+ u8 essid_len;
+ u8 bssid[ETH_ALEN];
+ u8 channel;
+ int last_mode;
+ int cstate_limit;
+
+ unsigned long connect_start;
+ unsigned long last_reset;
+
+ u32 channel_mask;
+ u32 fatal_error;
+ u32 fatal_errors[IPW2100_ERROR_QUEUE];
+ u32 fatal_index;
+ int eeprom_version;
+ int firmware_version;
+ unsigned long hw_features;
+ int hangs;
+ u32 last_rtc;
+ int dump_raw; /* 1 to dump raw bytes in /sys/.../memory */
+ u8* snapshot[0x30];
+
+ u8 mandatory_bssid_mac[ETH_ALEN];
+ u8 mac_addr[ETH_ALEN];
+
+ int power_mode;
+
+ /* WEP data */
+ struct ieee80211_security sec;
+ int messages_sent;
+
+
+ int short_retry_limit;
+ int long_retry_limit;
+
+ u32 rts_threshold;
+ u32 frag_threshold;
+
+ int in_isr;
+
+ u32 tx_rates;
+ int tx_power;
+ u32 beacon_interval;
+
+ char nick[IW_ESSID_MAX_SIZE + 1];
+
+ struct ipw2100_status_queue status_queue;
+
+ struct statistic txq_stat;
+ struct statistic rxq_stat;
+ struct ipw2100_bd_queue rx_queue;
+ struct ipw2100_bd_queue tx_queue;
+ struct ipw2100_rx_packet *rx_buffers;
+
+ struct statistic fw_pend_stat;
+ struct list_head fw_pend_list;
+
+ struct statistic msg_free_stat;
+ struct statistic msg_pend_stat;
+ struct list_head msg_free_list;
+ struct list_head msg_pend_list;
+ struct ipw2100_tx_packet *msg_buffers;
+
+ struct statistic tx_free_stat;
+ struct statistic tx_pend_stat;
+ struct list_head tx_free_list;
+ struct list_head tx_pend_list;
+ struct ipw2100_tx_packet *tx_buffers;
+
+ struct ipw2100_ordinals ordinals;
+
+ struct pci_dev *pci_dev;
+
+ struct proc_dir_entry *dir_dev;
+
+ struct net_device *net_dev;
+ struct iw_statistics wstats;
+
+ struct tasklet_struct irq_tasklet;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct reset_work;
+ struct work_struct security_work;
+ struct work_struct wx_event_work;
+ struct work_struct hang_check;
+ struct work_struct rf_kill;
+
+ u32 interrupts;
+ int tx_interrupts;
+ int rx_interrupts;
+ int inta_other;
+
+ spinlock_t low_lock;
+ struct semaphore action_sem;
+ struct semaphore adapter_sem;
+
+ wait_queue_head_t wait_command_queue;
+};
+
+
+/*********************************************************
+ * Host Command -> From Driver to FW
+ *********************************************************/
+
+/**
+ * Host command identifiers
+ */
+#define HOST_COMPLETE 2
+#define SYSTEM_CONFIG 6
+#define SSID 8
+#define MANDATORY_BSSID 9
+#define AUTHENTICATION_TYPE 10
+#define ADAPTER_ADDRESS 11
+#define PORT_TYPE 12
+#define INTERNATIONAL_MODE 13
+#define CHANNEL 14
+#define RTS_THRESHOLD 15
+#define FRAG_THRESHOLD 16
+#define POWER_MODE 17
+#define TX_RATES 18
+#define BASIC_TX_RATES 19
+#define WEP_KEY_INFO 20
+#define WEP_KEY_INDEX 25
+#define WEP_FLAGS 26
+#define ADD_MULTICAST 27
+#define CLEAR_ALL_MULTICAST 28
+#define BEACON_INTERVAL 29
+#define ATIM_WINDOW 30
+#define CLEAR_STATISTICS 31
+#define SEND 33
+#define TX_POWER_INDEX 36
+#define BROADCAST_SCAN 43
+#define CARD_DISABLE 44
+#define PREFERRED_BSSID 45
+#define SET_SCAN_OPTIONS 46
+#define SCAN_DWELL_TIME 47
+#define SWEEP_TABLE 48
+#define AP_OR_STATION_TABLE 49
+#define GROUP_ORDINALS 50
+#define SHORT_RETRY_LIMIT 51
+#define LONG_RETRY_LIMIT 52
+
+#define HOST_PRE_POWER_DOWN 58
+#define CARD_DISABLE_PHY_OFF 61
+#define MSDU_TX_RATES 62
+
+
+/* Rogue AP Detection */
+#define SET_STATION_STAT_BITS 64
+#define CLEAR_STATIONS_STAT_BITS 65
+#define LEAP_ROGUE_MODE 66 //TODO tbw replaced by CFG_LEAP_ROGUE_AP
+#define SET_SECURITY_INFORMATION 67
+#define DISASSOCIATION_BSSID 68
+#define SET_WPA_IE 69
+
+
+
+/* system configuration bit mask: */
+#define IPW_CFG_MONITOR 0x00004
+#define IPW_CFG_PREAMBLE_AUTO 0x00010
+#define IPW_CFG_IBSS_AUTO_START 0x00020
+#define IPW_CFG_LOOPBACK 0x00100
+#define IPW_CFG_ANSWER_BCSSID_PROBE 0x00800
+#define IPW_CFG_BT_SIDEBAND_SIGNAL 0x02000
+#define IPW_CFG_802_1x_ENABLE 0x04000
+#define IPW_CFG_BSS_MASK 0x08000
+#define IPW_CFG_IBSS_MASK 0x10000
+
+#define IPW_SCAN_NOASSOCIATE (1<<0)
+#define IPW_SCAN_MIXED_CELL (1<<1)
+/* RESERVED (1<<2) */
+#define IPW_SCAN_PASSIVE (1<<3)
+
+#define IPW_NIC_FATAL_ERROR 0x2A7F0
+#define IPW_ERROR_ADDR(x) (x & 0x3FFFF)
+#define IPW_ERROR_CODE(x) ((x & 0xFF000000) >> 24)
+#define IPW2100_ERR_C3_CORRUPTION (0x10 << 24)
+#define IPW2100_ERR_MSG_TIMEOUT (0x11 << 24)
+#define IPW2100_ERR_FW_LOAD (0x12 << 24)
+
+#define IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND 0x200
+#define IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x0D80
+
+#define IPW_MEM_HOST_SHARED_RX_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x40)
+#define IPW_MEM_HOST_SHARED_RX_STATUS_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x44)
+#define IPW_MEM_HOST_SHARED_RX_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x48)
+#define IPW_MEM_HOST_SHARED_RX_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0xa0)
+
+#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x00)
+#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x04)
+#define IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x80)
+
+#define IPW_MEM_HOST_SHARED_RX_WRITE_INDEX \
+ (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND + 0x20)
+
+#define IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX \
+ (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND)
+
+#define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1 (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x180)
+#define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2 (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x184)
+
+#define IPW2100_INTA_TX_TRANSFER (0x00000001) // Bit 0 (LSB)
+#define IPW2100_INTA_RX_TRANSFER (0x00000002) // Bit 1
+#define IPW2100_INTA_TX_COMPLETE (0x00000004) // Bit 2
+#define IPW2100_INTA_EVENT_INTERRUPT (0x00000008) // Bit 3
+#define IPW2100_INTA_STATUS_CHANGE (0x00000010) // Bit 4
+#define IPW2100_INTA_BEACON_PERIOD_EXPIRED (0x00000020) // Bit 5
+#define IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE (0x00010000) // Bit 16
+#define IPW2100_INTA_FW_INIT_DONE (0x01000000) // Bit 24
+#define IPW2100_INTA_FW_CALIBRATION_CALC (0x02000000) // Bit 25
+#define IPW2100_INTA_FATAL_ERROR (0x40000000) // Bit 30
+#define IPW2100_INTA_PARITY_ERROR (0x80000000) // Bit 31 (MSB)
+
+#define IPW_AUX_HOST_RESET_REG_PRINCETON_RESET (0x00000001)
+#define IPW_AUX_HOST_RESET_REG_FORCE_NMI (0x00000002)
+#define IPW_AUX_HOST_RESET_REG_PCI_HOST_CLUSTER_FATAL_NMI (0x00000004)
+#define IPW_AUX_HOST_RESET_REG_CORE_FATAL_NMI (0x00000008)
+#define IPW_AUX_HOST_RESET_REG_SW_RESET (0x00000080)
+#define IPW_AUX_HOST_RESET_REG_MASTER_DISABLED (0x00000100)
+#define IPW_AUX_HOST_RESET_REG_STOP_MASTER (0x00000200)
+
+#define IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY (0x00000001) // Bit 0 (LSB)
+#define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY (0x00000002) // Bit 1
+#define IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE (0x00000004) // Bit 2
+#define IPW_AUX_HOST_GP_CNTRL_BITS_SYS_CONFIG (0x000007c0) // Bits 6-10
+#define IPW_AUX_HOST_GP_CNTRL_BIT_BUS_TYPE (0x00000200) // Bit 9
+#define IPW_AUX_HOST_GP_CNTRL_BIT_BAR0_BLOCK_SIZE (0x00000400) // Bit 10
+#define IPW_AUX_HOST_GP_CNTRL_BIT_USB_MODE (0x20000000) // Bit 29
+#define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_FORCES_SYS_CLK (0x40000000) // Bit 30
+#define IPW_AUX_HOST_GP_CNTRL_BIT_FW_FORCES_SYS_CLK (0x80000000) // Bit 31 (MSB)
+
+#define IPW_BIT_GPIO_GPIO1_MASK 0x0000000C
+#define IPW_BIT_GPIO_GPIO3_MASK 0x000000C0
+#define IPW_BIT_GPIO_GPIO1_ENABLE 0x00000008
+#define IPW_BIT_GPIO_RF_KILL 0x00010000
+
+#define IPW_BIT_GPIO_LED_OFF 0x00002000 // Bit 13 = 1
+
+#define IPW_REG_DOMAIN_0_OFFSET 0x0000
+#define IPW_REG_DOMAIN_1_OFFSET IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND
+
+#define IPW_REG_INTA IPW_REG_DOMAIN_0_OFFSET + 0x0008
+#define IPW_REG_INTA_MASK IPW_REG_DOMAIN_0_OFFSET + 0x000C
+#define IPW_REG_INDIRECT_ACCESS_ADDRESS IPW_REG_DOMAIN_0_OFFSET + 0x0010
+#define IPW_REG_INDIRECT_ACCESS_DATA IPW_REG_DOMAIN_0_OFFSET + 0x0014
+#define IPW_REG_AUTOINCREMENT_ADDRESS IPW_REG_DOMAIN_0_OFFSET + 0x0018
+#define IPW_REG_AUTOINCREMENT_DATA IPW_REG_DOMAIN_0_OFFSET + 0x001C
+#define IPW_REG_RESET_REG IPW_REG_DOMAIN_0_OFFSET + 0x0020
+#define IPW_REG_GP_CNTRL IPW_REG_DOMAIN_0_OFFSET + 0x0024
+#define IPW_REG_GPIO IPW_REG_DOMAIN_0_OFFSET + 0x0030
+#define IPW_REG_FW_TYPE IPW_REG_DOMAIN_1_OFFSET + 0x0188
+#define IPW_REG_FW_VERSION IPW_REG_DOMAIN_1_OFFSET + 0x018C
+#define IPW_REG_FW_COMPATABILITY_VERSION IPW_REG_DOMAIN_1_OFFSET + 0x0190
+
+#define IPW_REG_INDIRECT_ADDR_MASK 0x00FFFFFC
+
+#define IPW_INTERRUPT_MASK 0xC1010013
+
+#define IPW2100_CONTROL_REG 0x220000
+#define IPW2100_CONTROL_PHY_OFF 0x8
+
+#define IPW2100_COMMAND 0x00300004
+#define IPW2100_COMMAND_PHY_ON 0x0
+#define IPW2100_COMMAND_PHY_OFF 0x1
+
+/* in DEBUG_AREA, values of memory always 0xd55555d5 */
+#define IPW_REG_DOA_DEBUG_AREA_START IPW_REG_DOMAIN_0_OFFSET + 0x0090
+#define IPW_REG_DOA_DEBUG_AREA_END IPW_REG_DOMAIN_0_OFFSET + 0x00FF
+#define IPW_DATA_DOA_DEBUG_VALUE 0xd55555d5
+
+#define IPW_INTERNAL_REGISTER_HALT_AND_RESET 0x003000e0
+
+#define IPW_WAIT_CLOCK_STABILIZATION_DELAY 50 // micro seconds
+#define IPW_WAIT_RESET_ARC_COMPLETE_DELAY 10 // micro seconds
+#define IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY 10 // micro seconds
+
+// BD ring queue read/write difference
+#define IPW_BD_QUEUE_W_R_MIN_SPARE 2
+
+#define IPW_CACHE_LINE_LENGTH_DEFAULT 0x80
+
+#define IPW_CARD_DISABLE_PHY_OFF_COMPLETE_WAIT 100 // 100 milli
+#define IPW_PREPARE_POWER_DOWN_COMPLETE_WAIT 100 // 100 milli
+
+
+
+
+#define IPW_HEADER_802_11_SIZE sizeof(struct ieee80211_hdr_3addr)
+#define IPW_MAX_80211_PAYLOAD_SIZE 2304U
+#define IPW_MAX_802_11_PAYLOAD_LENGTH 2312
+#define IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH 1536
+#define IPW_MIN_ACCEPTABLE_RX_FRAME_LENGTH 60
+#define IPW_MAX_ACCEPTABLE_RX_FRAME_LENGTH \
+ (IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH + IPW_HEADER_802_11_SIZE - \
+ sizeof(struct ethhdr))
+
+#define IPW_802_11_FCS_LENGTH 4
+#define IPW_RX_NIC_BUFFER_LENGTH \
+ (IPW_MAX_802_11_PAYLOAD_LENGTH + IPW_HEADER_802_11_SIZE + \
+ IPW_802_11_FCS_LENGTH)
+
+#define IPW_802_11_PAYLOAD_OFFSET \
+ (sizeof(struct ieee80211_hdr_3addr) + \
+ sizeof(struct ieee80211_snap_hdr))
+
+struct ipw2100_rx {
+ union {
+ unsigned char payload[IPW_RX_NIC_BUFFER_LENGTH];
+ struct ieee80211_hdr header;
+ u32 status;
+ struct ipw2100_notification notification;
+ struct ipw2100_cmd_header command;
+ } rx_data;
+} __attribute__ ((packed));
+
+/* Bit 0-7 are for 802.11b tx rates - . Bit 5-7 are reserved */
+#define TX_RATE_1_MBIT 0x0001
+#define TX_RATE_2_MBIT 0x0002
+#define TX_RATE_5_5_MBIT 0x0004
+#define TX_RATE_11_MBIT 0x0008
+#define TX_RATE_MASK 0x000F
+#define DEFAULT_TX_RATES 0x000F
+
+#define IPW_POWER_MODE_CAM 0x00 //(always on)
+#define IPW_POWER_INDEX_1 0x01
+#define IPW_POWER_INDEX_2 0x02
+#define IPW_POWER_INDEX_3 0x03
+#define IPW_POWER_INDEX_4 0x04
+#define IPW_POWER_INDEX_5 0x05
+#define IPW_POWER_AUTO 0x06
+#define IPW_POWER_MASK 0x0F
+#define IPW_POWER_ENABLED 0x10
+#define IPW_POWER_LEVEL(x) ((x) & IPW_POWER_MASK)
+
+#define IPW_TX_POWER_AUTO 0
+#define IPW_TX_POWER_ENHANCED 1
+
+#define IPW_TX_POWER_DEFAULT 32
+#define IPW_TX_POWER_MIN 0
+#define IPW_TX_POWER_MAX 16
+#define IPW_TX_POWER_MIN_DBM (-12)
+#define IPW_TX_POWER_MAX_DBM 16
+
+#define FW_SCAN_DONOT_ASSOCIATE 0x0001 // Dont Attempt to Associate after Scan
+#define FW_SCAN_PASSIVE 0x0008 // Force PASSSIVE Scan
+
+#define REG_MIN_CHANNEL 0
+#define REG_MAX_CHANNEL 14
+
+#define REG_CHANNEL_MASK 0x00003FFF
+#define IPW_IBSS_11B_DEFAULT_MASK 0x87ff
+
+#define DIVERSITY_EITHER 0 // Use both antennas
+#define DIVERSITY_ANTENNA_A 1 // Use antenna A
+#define DIVERSITY_ANTENNA_B 2 // Use antenna B
+
+
+#define HOST_COMMAND_WAIT 0
+#define HOST_COMMAND_NO_WAIT 1
+
+#define LOCK_NONE 0
+#define LOCK_DRIVER 1
+#define LOCK_FW 2
+
+#define TYPE_SWEEP_ORD 0x000D
+#define TYPE_IBSS_STTN_ORD 0x000E
+#define TYPE_BSS_AP_ORD 0x000F
+#define TYPE_RAW_BEACON_ENTRY 0x0010
+#define TYPE_CALIBRATION_DATA 0x0011
+#define TYPE_ROGUE_AP_DATA 0x0012
+#define TYPE_ASSOCIATION_REQUEST 0x0013
+#define TYPE_REASSOCIATION_REQUEST 0x0014
+
+
+#define HW_FEATURE_RFKILL (0x0001)
+#define RF_KILLSWITCH_OFF (1)
+#define RF_KILLSWITCH_ON (0)
+
+#define IPW_COMMAND_POOL_SIZE 40
+
+#define IPW_START_ORD_TAB_1 1
+#define IPW_START_ORD_TAB_2 1000
+
+#define IPW_ORD_TAB_1_ENTRY_SIZE sizeof(u32)
+
+#define IS_ORDINAL_TABLE_ONE(mgr,id) \
+ ((id >= IPW_START_ORD_TAB_1) && (id < mgr->table1_size))
+#define IS_ORDINAL_TABLE_TWO(mgr,id) \
+ ((id >= IPW_START_ORD_TAB_2) && (id < (mgr->table2_size + IPW_START_ORD_TAB_2)))
+
+#define BSS_ID_LENGTH 6
+
+// Fixed size data: Ordinal Table 1
+typedef enum _ORDINAL_TABLE_1 { // NS - means Not Supported by FW
+// Transmit statistics
+ IPW_ORD_STAT_TX_HOST_REQUESTS = 1,// # of requested Host Tx's (MSDU)
+ IPW_ORD_STAT_TX_HOST_COMPLETE, // # of successful Host Tx's (MSDU)
+ IPW_ORD_STAT_TX_DIR_DATA, // # of successful Directed Tx's (MSDU)
+
+ IPW_ORD_STAT_TX_DIR_DATA1 = 4, // # of successful Directed Tx's (MSDU) @ 1MB
+ IPW_ORD_STAT_TX_DIR_DATA2, // # of successful Directed Tx's (MSDU) @ 2MB
+ IPW_ORD_STAT_TX_DIR_DATA5_5, // # of successful Directed Tx's (MSDU) @ 5_5MB
+ IPW_ORD_STAT_TX_DIR_DATA11, // # of successful Directed Tx's (MSDU) @ 11MB
+ IPW_ORD_STAT_TX_DIR_DATA22, // # of successful Directed Tx's (MSDU) @ 22MB
+
+ IPW_ORD_STAT_TX_NODIR_DATA1 = 13,// # of successful Non_Directed Tx's (MSDU) @ 1MB
+ IPW_ORD_STAT_TX_NODIR_DATA2, // # of successful Non_Directed Tx's (MSDU) @ 2MB
+ IPW_ORD_STAT_TX_NODIR_DATA5_5, // # of successful Non_Directed Tx's (MSDU) @ 5.5MB
+ IPW_ORD_STAT_TX_NODIR_DATA11, // # of successful Non_Directed Tx's (MSDU) @ 11MB
+
+ IPW_ORD_STAT_NULL_DATA = 21, // # of successful NULL data Tx's
+ IPW_ORD_STAT_TX_RTS, // # of successful Tx RTS
+ IPW_ORD_STAT_TX_CTS, // # of successful Tx CTS
+ IPW_ORD_STAT_TX_ACK, // # of successful Tx ACK
+ IPW_ORD_STAT_TX_ASSN, // # of successful Association Tx's
+ IPW_ORD_STAT_TX_ASSN_RESP, // # of successful Association response Tx's
+ IPW_ORD_STAT_TX_REASSN, // # of successful Reassociation Tx's
+ IPW_ORD_STAT_TX_REASSN_RESP, // # of successful Reassociation response Tx's
+ IPW_ORD_STAT_TX_PROBE, // # of probes successfully transmitted
+ IPW_ORD_STAT_TX_PROBE_RESP, // # of probe responses successfully transmitted
+ IPW_ORD_STAT_TX_BEACON, // # of tx beacon
+ IPW_ORD_STAT_TX_ATIM, // # of Tx ATIM
+ IPW_ORD_STAT_TX_DISASSN, // # of successful Disassociation TX
+ IPW_ORD_STAT_TX_AUTH, // # of successful Authentication Tx
+ IPW_ORD_STAT_TX_DEAUTH, // # of successful Deauthentication TX
+
+ IPW_ORD_STAT_TX_TOTAL_BYTES = 41,// Total successful Tx data bytes
+ IPW_ORD_STAT_TX_RETRIES, // # of Tx retries
+ IPW_ORD_STAT_TX_RETRY1, // # of Tx retries at 1MBPS
+ IPW_ORD_STAT_TX_RETRY2, // # of Tx retries at 2MBPS
+ IPW_ORD_STAT_TX_RETRY5_5, // # of Tx retries at 5.5MBPS
+ IPW_ORD_STAT_TX_RETRY11, // # of Tx retries at 11MBPS
+
+ IPW_ORD_STAT_TX_FAILURES = 51, // # of Tx Failures
+ IPW_ORD_STAT_TX_ABORT_AT_HOP, //NS // # of Tx's aborted at hop time
+ IPW_ORD_STAT_TX_MAX_TRIES_IN_HOP,// # of times max tries in a hop failed
+ IPW_ORD_STAT_TX_ABORT_LATE_DMA, //NS // # of times tx aborted due to late dma setup
+ IPW_ORD_STAT_TX_ABORT_STX, //NS // # of times backoff aborted
+ IPW_ORD_STAT_TX_DISASSN_FAIL, // # of times disassociation failed
+ IPW_ORD_STAT_TX_ERR_CTS, // # of missed/bad CTS frames
+ IPW_ORD_STAT_TX_BPDU, //NS // # of spanning tree BPDUs sent
+ IPW_ORD_STAT_TX_ERR_ACK, // # of tx err due to acks
+
+ // Receive statistics
+ IPW_ORD_STAT_RX_HOST = 61, // # of packets passed to host
+ IPW_ORD_STAT_RX_DIR_DATA, // # of directed packets
+ IPW_ORD_STAT_RX_DIR_DATA1, // # of directed packets at 1MB
+ IPW_ORD_STAT_RX_DIR_DATA2, // # of directed packets at 2MB
+ IPW_ORD_STAT_RX_DIR_DATA5_5, // # of directed packets at 5.5MB
+ IPW_ORD_STAT_RX_DIR_DATA11, // # of directed packets at 11MB
+ IPW_ORD_STAT_RX_DIR_DATA22, // # of directed packets at 22MB
+
+ IPW_ORD_STAT_RX_NODIR_DATA = 71,// # of nondirected packets
+ IPW_ORD_STAT_RX_NODIR_DATA1, // # of nondirected packets at 1MB
+ IPW_ORD_STAT_RX_NODIR_DATA2, // # of nondirected packets at 2MB
+ IPW_ORD_STAT_RX_NODIR_DATA5_5, // # of nondirected packets at 5.5MB
+ IPW_ORD_STAT_RX_NODIR_DATA11, // # of nondirected packets at 11MB
+
+ IPW_ORD_STAT_RX_NULL_DATA = 80, // # of null data rx's
+ IPW_ORD_STAT_RX_POLL, //NS // # of poll rx
+ IPW_ORD_STAT_RX_RTS, // # of Rx RTS
+ IPW_ORD_STAT_RX_CTS, // # of Rx CTS
+ IPW_ORD_STAT_RX_ACK, // # of Rx ACK
+ IPW_ORD_STAT_RX_CFEND, // # of Rx CF End
+ IPW_ORD_STAT_RX_CFEND_ACK, // # of Rx CF End + CF Ack
+ IPW_ORD_STAT_RX_ASSN, // # of Association Rx's
+ IPW_ORD_STAT_RX_ASSN_RESP, // # of Association response Rx's
+ IPW_ORD_STAT_RX_REASSN, // # of Reassociation Rx's
+ IPW_ORD_STAT_RX_REASSN_RESP, // # of Reassociation response Rx's
+ IPW_ORD_STAT_RX_PROBE, // # of probe Rx's
+ IPW_ORD_STAT_RX_PROBE_RESP, // # of probe response Rx's
+ IPW_ORD_STAT_RX_BEACON, // # of Rx beacon
+ IPW_ORD_STAT_RX_ATIM, // # of Rx ATIM
+ IPW_ORD_STAT_RX_DISASSN, // # of disassociation Rx
+ IPW_ORD_STAT_RX_AUTH, // # of authentication Rx
+ IPW_ORD_STAT_RX_DEAUTH, // # of deauthentication Rx
+
+ IPW_ORD_STAT_RX_TOTAL_BYTES = 101,// Total rx data bytes received
+ IPW_ORD_STAT_RX_ERR_CRC, // # of packets with Rx CRC error
+ IPW_ORD_STAT_RX_ERR_CRC1, // # of Rx CRC errors at 1MB
+ IPW_ORD_STAT_RX_ERR_CRC2, // # of Rx CRC errors at 2MB
+ IPW_ORD_STAT_RX_ERR_CRC5_5, // # of Rx CRC errors at 5.5MB
+ IPW_ORD_STAT_RX_ERR_CRC11, // # of Rx CRC errors at 11MB
+
+ IPW_ORD_STAT_RX_DUPLICATE1 = 112, // # of duplicate rx packets at 1MB
+ IPW_ORD_STAT_RX_DUPLICATE2, // # of duplicate rx packets at 2MB
+ IPW_ORD_STAT_RX_DUPLICATE5_5, // # of duplicate rx packets at 5.5MB
+ IPW_ORD_STAT_RX_DUPLICATE11, // # of duplicate rx packets at 11MB
+ IPW_ORD_STAT_RX_DUPLICATE = 119, // # of duplicate rx packets
+
+ IPW_ORD_PERS_DB_LOCK = 120, // # locking fw permanent db
+ IPW_ORD_PERS_DB_SIZE, // # size of fw permanent db
+ IPW_ORD_PERS_DB_ADDR, // # address of fw permanent db
+ IPW_ORD_STAT_RX_INVALID_PROTOCOL, // # of rx frames with invalid protocol
+ IPW_ORD_SYS_BOOT_TIME, // # Boot time
+ IPW_ORD_STAT_RX_NO_BUFFER, // # of rx frames rejected due to no buffer
+ IPW_ORD_STAT_RX_ABORT_LATE_DMA, //NS // # of rx frames rejected due to dma setup too late
+ IPW_ORD_STAT_RX_ABORT_AT_HOP, //NS // # of rx frames aborted due to hop
+ IPW_ORD_STAT_RX_MISSING_FRAG, // # of rx frames dropped due to missing fragment
+ IPW_ORD_STAT_RX_ORPHAN_FRAG, // # of rx frames dropped due to non-sequential fragment
+ IPW_ORD_STAT_RX_ORPHAN_FRAME, // # of rx frames dropped due to unmatched 1st frame
+ IPW_ORD_STAT_RX_FRAG_AGEOUT, // # of rx frames dropped due to uncompleted frame
+ IPW_ORD_STAT_RX_BAD_SSID, //NS // Bad SSID (unused)
+ IPW_ORD_STAT_RX_ICV_ERRORS, // # of ICV errors during decryption
+
+// PSP Statistics
+ IPW_ORD_STAT_PSP_SUSPENSION = 137,// # of times adapter suspended
+ IPW_ORD_STAT_PSP_BCN_TIMEOUT, // # of beacon timeout
+ IPW_ORD_STAT_PSP_POLL_TIMEOUT, // # of poll response timeouts
+ IPW_ORD_STAT_PSP_NONDIR_TIMEOUT,// # of timeouts waiting for last broadcast/muticast pkt
+ IPW_ORD_STAT_PSP_RX_DTIMS, // # of PSP DTIMs received
+ IPW_ORD_STAT_PSP_RX_TIMS, // # of PSP TIMs received
+ IPW_ORD_STAT_PSP_STATION_ID, // PSP Station ID
+
+// Association and roaming
+ IPW_ORD_LAST_ASSN_TIME = 147, // RTC time of last association
+ IPW_ORD_STAT_PERCENT_MISSED_BCNS,// current calculation of % missed beacons
+ IPW_ORD_STAT_PERCENT_RETRIES, // current calculation of % missed tx retries
+ IPW_ORD_ASSOCIATED_AP_PTR, // If associated, this is ptr to the associated
+ // AP table entry. set to 0 if not associated
+ IPW_ORD_AVAILABLE_AP_CNT, // # of AP's decsribed in the AP table
+ IPW_ORD_AP_LIST_PTR, // Ptr to list of available APs
+ IPW_ORD_STAT_AP_ASSNS, // # of associations
+ IPW_ORD_STAT_ASSN_FAIL, // # of association failures
+ IPW_ORD_STAT_ASSN_RESP_FAIL, // # of failuresdue to response fail
+ IPW_ORD_STAT_FULL_SCANS, // # of full scans
+
+ IPW_ORD_CARD_DISABLED, // # Card Disabled
+ IPW_ORD_STAT_ROAM_INHIBIT, // # of times roaming was inhibited due to ongoing activity
+ IPW_FILLER_40,
+ IPW_ORD_RSSI_AT_ASSN = 160, // RSSI of associated AP at time of association
+ IPW_ORD_STAT_ASSN_CAUSE1, // # of reassociations due to no tx from AP in last N
+ // hops or no prob_ responses in last 3 minutes
+ IPW_ORD_STAT_ASSN_CAUSE2, // # of reassociations due to poor tx/rx quality
+ IPW_ORD_STAT_ASSN_CAUSE3, // # of reassociations due to tx/rx quality with excessive
+ // load at the AP
+ IPW_ORD_STAT_ASSN_CAUSE4, // # of reassociations due to AP RSSI level fell below
+ // eligible group
+ IPW_ORD_STAT_ASSN_CAUSE5, // # of reassociations due to load leveling
+ IPW_ORD_STAT_ASSN_CAUSE6, //NS // # of reassociations due to dropped by Ap
+ IPW_FILLER_41,
+ IPW_FILLER_42,
+ IPW_FILLER_43,
+ IPW_ORD_STAT_AUTH_FAIL, // # of times authentication failed
+ IPW_ORD_STAT_AUTH_RESP_FAIL, // # of times authentication response failed
+ IPW_ORD_STATION_TABLE_CNT, // # of entries in association table
+
+// Other statistics
+ IPW_ORD_RSSI_AVG_CURR = 173, // Current avg RSSI
+ IPW_ORD_STEST_RESULTS_CURR, //NS // Current self test results word
+ IPW_ORD_STEST_RESULTS_CUM, //NS // Cummulative self test results word
+ IPW_ORD_SELF_TEST_STATUS, //NS //
+ IPW_ORD_POWER_MGMT_MODE, // Power mode - 0=CAM, 1=PSP
+ IPW_ORD_POWER_MGMT_INDEX, //NS //
+ IPW_ORD_COUNTRY_CODE, // IEEE country code as recv'd from beacon
+ IPW_ORD_COUNTRY_CHANNELS, // channels suported by country
+// IPW_ORD_COUNTRY_CHANNELS:
+// For 11b the lower 2-byte are used for channels from 1-14
+// and the higher 2-byte are not used.
+ IPW_ORD_RESET_CNT, // # of adapter resets (warm)
+ IPW_ORD_BEACON_INTERVAL, // Beacon interval
+
+ IPW_ORD_PRINCETON_VERSION = 184, //NS // Princeton Version
+ IPW_ORD_ANTENNA_DIVERSITY, // TRUE if antenna diversity is disabled
+ IPW_ORD_CCA_RSSI, //NS // CCA RSSI value (factory programmed)
+ IPW_ORD_STAT_EEPROM_UPDATE, //NS // # of times config EEPROM updated
+ IPW_ORD_DTIM_PERIOD, // # of beacon intervals between DTIMs
+ IPW_ORD_OUR_FREQ, // current radio freq lower digits - channel ID
+
+ IPW_ORD_RTC_TIME = 190, // current RTC time
+ IPW_ORD_PORT_TYPE, // operating mode
+ IPW_ORD_CURRENT_TX_RATE, // current tx rate
+ IPW_ORD_SUPPORTED_RATES, // Bitmap of supported tx rates
+ IPW_ORD_ATIM_WINDOW, // current ATIM Window
+ IPW_ORD_BASIC_RATES, // bitmap of basic tx rates
+ IPW_ORD_NIC_HIGHEST_RATE, // bitmap of basic tx rates
+ IPW_ORD_AP_HIGHEST_RATE, // bitmap of basic tx rates
+ IPW_ORD_CAPABILITIES, // Management frame capability field
+ IPW_ORD_AUTH_TYPE, // Type of authentication
+ IPW_ORD_RADIO_TYPE, // Adapter card platform type
+ IPW_ORD_RTS_THRESHOLD = 201, // Min length of packet after which RTS handshaking is used
+ IPW_ORD_INT_MODE, // International mode
+ IPW_ORD_FRAGMENTATION_THRESHOLD, // protocol frag threshold
+ IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, // EEPROM offset in SRAM
+ IPW_ORD_EEPROM_SRAM_DB_BLOCK_SIZE, // EEPROM size in SRAM
+ IPW_ORD_EEPROM_SKU_CAPABILITY, // EEPROM SKU Capability 206 =
+ IPW_ORD_EEPROM_IBSS_11B_CHANNELS, // EEPROM IBSS 11b channel set
+
+ IPW_ORD_MAC_VERSION = 209, // MAC Version
+ IPW_ORD_MAC_REVISION, // MAC Revision
+ IPW_ORD_RADIO_VERSION, // Radio Version
+ IPW_ORD_NIC_MANF_DATE_TIME, // MANF Date/Time STAMP
+ IPW_ORD_UCODE_VERSION, // Ucode Version
+ IPW_ORD_HW_RF_SWITCH_STATE = 214, // HW RF Kill Switch State
+} ORDINALTABLE1;
+
+// ordinal table 2
+// Variable length data:
+#define IPW_FIRST_VARIABLE_LENGTH_ORDINAL 1001
+
+typedef enum _ORDINAL_TABLE_2 { // NS - means Not Supported by FW
+ IPW_ORD_STAT_BASE = 1000, // contains number of variable ORDs
+ IPW_ORD_STAT_ADAPTER_MAC = 1001, // 6 bytes: our adapter MAC address
+ IPW_ORD_STAT_PREFERRED_BSSID = 1002, // 6 bytes: BSSID of the preferred AP
+ IPW_ORD_STAT_MANDATORY_BSSID = 1003, // 6 bytes: BSSID of the mandatory AP
+ IPW_FILL_1, //NS //
+ IPW_ORD_STAT_COUNTRY_TEXT = 1005, // 36 bytes: Country name text, First two bytes are Country code
+ IPW_ORD_STAT_ASSN_SSID = 1006, // 32 bytes: ESSID String
+ IPW_ORD_STATION_TABLE = 1007, // ? bytes: Station/AP table (via Direct SSID Scans)
+ IPW_ORD_STAT_SWEEP_TABLE = 1008, // ? bytes: Sweep/Host Table table (via Broadcast Scans)
+ IPW_ORD_STAT_ROAM_LOG = 1009, // ? bytes: Roaming log
+ IPW_ORD_STAT_RATE_LOG = 1010, //NS // 0 bytes: Rate log
+ IPW_ORD_STAT_FIFO = 1011, //NS // 0 bytes: Fifo buffer data structures
+ IPW_ORD_STAT_FW_VER_NUM = 1012, // 14 bytes: fw version ID string as in (a.bb.ccc; "0.08.011")
+ IPW_ORD_STAT_FW_DATE = 1013, // 14 bytes: fw date string (mmm dd yyyy; "Mar 13 2002")
+ IPW_ORD_STAT_ASSN_AP_BSSID = 1014, // 6 bytes: MAC address of associated AP
+ IPW_ORD_STAT_DEBUG = 1015, //NS // ? bytes:
+ IPW_ORD_STAT_NIC_BPA_NUM = 1016, // 11 bytes: NIC BPA number in ASCII
+ IPW_ORD_STAT_UCODE_DATE = 1017, // 5 bytes: uCode date
+ IPW_ORD_SECURITY_NGOTIATION_RESULT = 1018,
+} ORDINALTABLE2; // NS - means Not Supported by FW
+
+#define IPW_LAST_VARIABLE_LENGTH_ORDINAL 1018
+
+#ifndef WIRELESS_SPY
+#define WIRELESS_SPY // enable iwspy support
+#endif
+
+#define IPW_HOST_FW_SHARED_AREA0 0x0002f200
+#define IPW_HOST_FW_SHARED_AREA0_END 0x0002f510 // 0x310 bytes
+
+#define IPW_HOST_FW_SHARED_AREA1 0x0002f610
+#define IPW_HOST_FW_SHARED_AREA1_END 0x0002f630 // 0x20 bytes
+
+#define IPW_HOST_FW_SHARED_AREA2 0x0002fa00
+#define IPW_HOST_FW_SHARED_AREA2_END 0x0002fa20 // 0x20 bytes
+
+#define IPW_HOST_FW_SHARED_AREA3 0x0002fc00
+#define IPW_HOST_FW_SHARED_AREA3_END 0x0002fc10 // 0x10 bytes
+
+#define IPW_HOST_FW_INTERRUPT_AREA 0x0002ff80
+#define IPW_HOST_FW_INTERRUPT_AREA_END 0x00030000 // 0x80 bytes
+
+struct ipw2100_fw_chunk {
+ unsigned char *buf;
+ long len;
+ long pos;
+ struct list_head list;
+};
+
+struct ipw2100_fw_chunk_set {
+ const void *data;
+ unsigned long size;
+};
+
+struct ipw2100_fw {
+ int version;
+ struct ipw2100_fw_chunk_set fw;
+ struct ipw2100_fw_chunk_set uc;
+ const struct firmware *fw_entry;
+};
+
+#define MAX_FW_VERSION_LEN 14
+
+#endif /* _IPW2100_H */
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
new file mode 100644
index 000000000000..6d0b6b1df4ca
--- /dev/null
+++ b/drivers/net/wireless/ipw2200.c
@@ -0,0 +1,7353 @@
+/******************************************************************************
+
+ Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved.
+
+ 802.11 status code portion of this file from ethereal-0.10.6:
+ Copyright 2000, Axis Communications AB
+ Ethereal - Network traffic analyzer
+ By Gerald Combs <gerald@ethereal.com>
+ Copyright 1998 Gerald Combs
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************/
+
+#include "ipw2200.h"
+
+#define IPW2200_VERSION "1.0.0"
+#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
+#define DRV_COPYRIGHT "Copyright(c) 2003-2004 Intel Corporation"
+#define DRV_VERSION IPW2200_VERSION
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+static int channel = 0;
+static char *ifname;
+static int mode = 0;
+
+static u32 ipw_debug_level;
+static int associate = 1;
+static int auto_create = 1;
+static int disable = 0;
+static const char ipw_modes[] = {
+ 'a', 'b', 'g', '?'
+};
+
+static void ipw_rx(struct ipw_priv *priv);
+static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
+ struct clx2_tx_queue *txq, int qindex);
+static int ipw_queue_reset(struct ipw_priv *priv);
+
+static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
+ int len, int sync);
+
+static void ipw_tx_queue_free(struct ipw_priv *);
+
+static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
+static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
+static void ipw_rx_queue_replenish(void *);
+
+static int ipw_up(struct ipw_priv *);
+static void ipw_down(struct ipw_priv *);
+static int ipw_config(struct ipw_priv *);
+static int init_supported_rates(struct ipw_priv *priv, struct ipw_supported_rates *prates);
+
+static u8 band_b_active_channel[MAX_B_CHANNELS] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0
+};
+static u8 band_a_active_channel[MAX_A_CHANNELS] = {
+ 36, 40, 44, 48, 149, 153, 157, 161, 165, 52, 56, 60, 64, 0
+};
+
+static int is_valid_channel(int mode_mask, int channel)
+{
+ int i;
+
+ if (!channel)
+ return 0;
+
+ if (mode_mask & IEEE_A)
+ for (i = 0; i < MAX_A_CHANNELS; i++)
+ if (band_a_active_channel[i] == channel)
+ return IEEE_A;
+
+ if (mode_mask & (IEEE_B | IEEE_G))
+ for (i = 0; i < MAX_B_CHANNELS; i++)
+ if (band_b_active_channel[i] == channel)
+ return mode_mask & (IEEE_B | IEEE_G);
+
+ return 0;
+}
+
+static char *snprint_line(char *buf, size_t count,
+ const u8 *data, u32 len, u32 ofs)
+{
+ int out, i, j, l;
+ char c;
+
+ out = snprintf(buf, count, "%08X", ofs);
+
+ for (l = 0, i = 0; i < 2; i++) {
+ out += snprintf(buf + out, count - out, " ");
+ for (j = 0; j < 8 && l < len; j++, l++)
+ out += snprintf(buf + out, count - out, "%02X ",
+ data[(i * 8 + j)]);
+ for (; j < 8; j++)
+ out += snprintf(buf + out, count - out, " ");
+ }
+
+ out += snprintf(buf + out, count - out, " ");
+ for (l = 0, i = 0; i < 2; i++) {
+ out += snprintf(buf + out, count - out, " ");
+ for (j = 0; j < 8 && l < len; j++, l++) {
+ c = data[(i * 8 + j)];
+ if (!isascii(c) || !isprint(c))
+ c = '.';
+
+ out += snprintf(buf + out, count - out, "%c", c);
+ }
+
+ for (; j < 8; j++)
+ out += snprintf(buf + out, count - out, " ");
+ }
+
+ return buf;
+}
+
+static void printk_buf(int level, const u8 *data, u32 len)
+{
+ char line[81];
+ u32 ofs = 0;
+ if (!(ipw_debug_level & level))
+ return;
+
+ while (len) {
+ printk(KERN_DEBUG "%s\n",
+ snprint_line(line, sizeof(line), &data[ofs],
+ min(len, 16U), ofs));
+ ofs += 16;
+ len -= min(len, 16U);
+ }
+}
+
+static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
+#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
+
+static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
+#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
+
+static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
+static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
+{
+ IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(b), (u32)(c));
+ _ipw_write_reg8(a, b, c);
+}
+
+static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
+static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
+{
+ IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(b), (u32)(c));
+ _ipw_write_reg16(a, b, c);
+}
+
+static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
+static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
+{
+ IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(b), (u32)(c));
+ _ipw_write_reg32(a, b, c);
+}
+
+#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))
+#define ipw_write8(ipw, ofs, val) \
+ IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
+ _ipw_write8(ipw, ofs, val)
+
+#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))
+#define ipw_write16(ipw, ofs, val) \
+ IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
+ _ipw_write16(ipw, ofs, val)
+
+#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
+#define ipw_write32(ipw, ofs, val) \
+ IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
+ _ipw_write32(ipw, ofs, val)
+
+#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))
+static inline u8 __ipw_read8(char *f, u32 l, struct ipw_priv *ipw, u32 ofs) {
+ IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", f, l, (u32)(ofs));
+ return _ipw_read8(ipw, ofs);
+}
+#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)
+
+#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))
+static inline u16 __ipw_read16(char *f, u32 l, struct ipw_priv *ipw, u32 ofs) {
+ IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", f, l, (u32)(ofs));
+ return _ipw_read16(ipw, ofs);
+}
+#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)
+
+#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
+static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs) {
+ IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", f, l, (u32)(ofs));
+ return _ipw_read32(ipw, ofs);
+}
+#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
+
+static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
+#define ipw_read_indirect(a, b, c, d) \
+ IPW_DEBUG_IO("%s %d: read_inddirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
+ _ipw_read_indirect(a, b, c, d)
+
+static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 *data, int num);
+#define ipw_write_indirect(a, b, c, d) \
+ IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
+ _ipw_write_indirect(a, b, c, d)
+
+/* indirect write s */
+static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg,
+ u32 value)
+{
+ IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n",
+ priv, reg, value);
+ _ipw_write32(priv, CX2_INDIRECT_ADDR, reg);
+ _ipw_write32(priv, CX2_INDIRECT_DATA, value);
+}
+
+
+static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
+{
+ IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
+ _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK);
+ _ipw_write8(priv, CX2_INDIRECT_DATA, value);
+ IPW_DEBUG_IO(" reg = 0x%8lX : value = 0x%8X\n",
+ (unsigned long)(priv->hw_base + CX2_INDIRECT_DATA),
+ value);
+}
+
+static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg,
+ u16 value)
+{
+ IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
+ _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK);
+ _ipw_write16(priv, CX2_INDIRECT_DATA, value);
+}
+
+/* indirect read s */
+
+static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
+{
+ u32 word;
+ _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK);
+ IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
+ word = _ipw_read32(priv, CX2_INDIRECT_DATA);
+ return (word >> ((reg & 0x3)*8)) & 0xff;
+}
+
+static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
+{
+ u32 value;
+
+ IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);
+
+ _ipw_write32(priv, CX2_INDIRECT_ADDR, reg);
+ value = _ipw_read32(priv, CX2_INDIRECT_DATA);
+ IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
+ return value;
+}
+
+/* iterative/auto-increment 32 bit reads and writes */
+static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
+ int num)
+{
+ u32 aligned_addr = addr & CX2_INDIRECT_ADDR_MASK;
+ u32 dif_len = addr - aligned_addr;
+ u32 aligned_len;
+ u32 i;
+
+ IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
+
+ /* Read the first nibble byte by byte */
+ if (unlikely(dif_len)) {
+ /* Start reading at aligned_addr + dif_len */
+ _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
+ for (i = dif_len; i < 4; i++, buf++)
+ *buf = _ipw_read8(priv, CX2_INDIRECT_DATA + i);
+ num -= dif_len;
+ aligned_addr += 4;
+ }
+
+ /* Read DWs through autoinc register */
+ _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr);
+ aligned_len = num & CX2_INDIRECT_ADDR_MASK;
+ for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+ *(u32*)buf = ipw_read32(priv, CX2_AUTOINC_DATA);
+
+ /* Copy the last nibble */
+ dif_len = num - aligned_len;
+ _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
+ for (i = 0; i < dif_len; i++, buf++)
+ *buf = ipw_read8(priv, CX2_INDIRECT_DATA + i);
+}
+
+static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 *buf,
+ int num)
+{
+ u32 aligned_addr = addr & CX2_INDIRECT_ADDR_MASK;
+ u32 dif_len = addr - aligned_addr;
+ u32 aligned_len;
+ u32 i;
+
+ IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
+
+ /* Write the first nibble byte by byte */
+ if (unlikely(dif_len)) {
+ /* Start writing at aligned_addr + dif_len */
+ _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
+ for (i = dif_len; i < 4; i++, buf++)
+ _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf);
+ num -= dif_len;
+ aligned_addr += 4;
+ }
+
+ /* Write DWs through autoinc register */
+ _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr);
+ aligned_len = num & CX2_INDIRECT_ADDR_MASK;
+ for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+ _ipw_write32(priv, CX2_AUTOINC_DATA, *(u32*)buf);
+
+ /* Copy the last nibble */
+ dif_len = num - aligned_len;
+ _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr);
+ for (i = 0; i < dif_len; i++, buf++)
+ _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf);
+}
+
+static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
+ int num)
+{
+ memcpy_toio((priv->hw_base + addr), buf, num);
+}
+
+static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
+{
+ ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
+}
+
+static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
+{
+ ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
+}
+
+static inline void ipw_enable_interrupts(struct ipw_priv *priv)
+{
+ if (priv->status & STATUS_INT_ENABLED)
+ return;
+ priv->status |= STATUS_INT_ENABLED;
+ ipw_write32(priv, CX2_INTA_MASK_R, CX2_INTA_MASK_ALL);
+}
+
+static inline void ipw_disable_interrupts(struct ipw_priv *priv)
+{
+ if (!(priv->status & STATUS_INT_ENABLED))
+ return;
+ priv->status &= ~STATUS_INT_ENABLED;
+ ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
+}
+
+static char *ipw_error_desc(u32 val)
+{
+ switch (val) {
+ case IPW_FW_ERROR_OK:
+ return "ERROR_OK";
+ case IPW_FW_ERROR_FAIL:
+ return "ERROR_FAIL";
+ case IPW_FW_ERROR_MEMORY_UNDERFLOW:
+ return "MEMORY_UNDERFLOW";
+ case IPW_FW_ERROR_MEMORY_OVERFLOW:
+ return "MEMORY_OVERFLOW";
+ case IPW_FW_ERROR_BAD_PARAM:
+ return "ERROR_BAD_PARAM";
+ case IPW_FW_ERROR_BAD_CHECKSUM:
+ return "ERROR_BAD_CHECKSUM";
+ case IPW_FW_ERROR_NMI_INTERRUPT:
+ return "ERROR_NMI_INTERRUPT";
+ case IPW_FW_ERROR_BAD_DATABASE:
+ return "ERROR_BAD_DATABASE";
+ case IPW_FW_ERROR_ALLOC_FAIL:
+ return "ERROR_ALLOC_FAIL";
+ case IPW_FW_ERROR_DMA_UNDERRUN:
+ return "ERROR_DMA_UNDERRUN";
+ case IPW_FW_ERROR_DMA_STATUS:
+ return "ERROR_DMA_STATUS";
+ case IPW_FW_ERROR_DINOSTATUS_ERROR:
+ return "ERROR_DINOSTATUS_ERROR";
+ case IPW_FW_ERROR_EEPROMSTATUS_ERROR:
+ return "ERROR_EEPROMSTATUS_ERROR";
+ case IPW_FW_ERROR_SYSASSERT:
+ return "ERROR_SYSASSERT";
+ case IPW_FW_ERROR_FATAL_ERROR:
+ return "ERROR_FATALSTATUS_ERROR";
+ default:
+ return "UNKNOWNSTATUS_ERROR";
+ }
+}
+
+static void ipw_dump_nic_error_log(struct ipw_priv *priv)
+{
+ u32 desc, time, blink1, blink2, ilink1, ilink2, idata, i, count, base;
+
+ base = ipw_read32(priv, IPWSTATUS_ERROR_LOG);
+ count = ipw_read_reg32(priv, base);
+
+ if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
+ IPW_ERROR("Start IPW Error Log Dump:\n");
+ IPW_ERROR("Status: 0x%08X, Config: %08X\n",
+ priv->status, priv->config);
+ }
+
+ for (i = ERROR_START_OFFSET;
+ i <= count * ERROR_ELEM_SIZE;
+ i += ERROR_ELEM_SIZE) {
+ desc = ipw_read_reg32(priv, base + i);
+ time = ipw_read_reg32(priv, base + i + 1*sizeof(u32));
+ blink1 = ipw_read_reg32(priv, base + i + 2*sizeof(u32));
+ blink2 = ipw_read_reg32(priv, base + i + 3*sizeof(u32));
+ ilink1 = ipw_read_reg32(priv, base + i + 4*sizeof(u32));
+ ilink2 = ipw_read_reg32(priv, base + i + 5*sizeof(u32));
+ idata = ipw_read_reg32(priv, base + i + 6*sizeof(u32));
+
+ IPW_ERROR(
+ "%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ ipw_error_desc(desc), time, blink1, blink2,
+ ilink1, ilink2, idata);
+ }
+}
+
+static void ipw_dump_nic_event_log(struct ipw_priv *priv)
+{
+ u32 ev, time, data, i, count, base;
+
+ base = ipw_read32(priv, IPW_EVENT_LOG);
+ count = ipw_read_reg32(priv, base);
+
+ if (EVENT_START_OFFSET <= count * EVENT_ELEM_SIZE)
+ IPW_ERROR("Start IPW Event Log Dump:\n");
+
+ for (i = EVENT_START_OFFSET;
+ i <= count * EVENT_ELEM_SIZE;
+ i += EVENT_ELEM_SIZE) {
+ ev = ipw_read_reg32(priv, base + i);
+ time = ipw_read_reg32(priv, base + i + 1*sizeof(u32));
+ data = ipw_read_reg32(priv, base + i + 2*sizeof(u32));
+
+#ifdef CONFIG_IPW_DEBUG
+ IPW_ERROR("%i\t0x%08x\t%i\n", time, data, ev);
+#endif
+ }
+}
+
+static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val,
+ u32 *len)
+{
+ u32 addr, field_info, field_len, field_count, total_len;
+
+ IPW_DEBUG_ORD("ordinal = %i\n", ord);
+
+ if (!priv || !val || !len) {
+ IPW_DEBUG_ORD("Invalid argument\n");
+ return -EINVAL;
+ }
+
+ /* verify device ordinal tables have been initialized */
+ if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
+ IPW_DEBUG_ORD("Access ordinals before initialization\n");
+ return -EINVAL;
+ }
+
+ switch (IPW_ORD_TABLE_ID_MASK & ord) {
+ case IPW_ORD_TABLE_0_MASK:
+ /*
+ * TABLE 0: Direct access to a table of 32 bit values
+ *
+ * This is a very simple table with the data directly
+ * read from the table
+ */
+
+ /* remove the table id from the ordinal */
+ ord &= IPW_ORD_TABLE_VALUE_MASK;
+
+ /* boundary check */
+ if (ord > priv->table0_len) {
+ IPW_DEBUG_ORD("ordinal value (%i) longer then "
+ "max (%i)\n", ord, priv->table0_len);
+ return -EINVAL;
+ }
+
+ /* verify we have enough room to store the value */
+ if (*len < sizeof(u32)) {
+ IPW_DEBUG_ORD("ordinal buffer length too small, "
+ "need %zd\n", sizeof(u32));
+ return -EINVAL;
+ }
+
+ IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
+ ord, priv->table0_addr + (ord << 2));
+
+ *len = sizeof(u32);
+ ord <<= 2;
+ *((u32 *)val) = ipw_read32(priv, priv->table0_addr + ord);
+ break;
+
+ case IPW_ORD_TABLE_1_MASK:
+ /*
+ * TABLE 1: Indirect access to a table of 32 bit values
+ *
+ * This is a fairly large table of u32 values each
+ * representing starting addr for the data (which is
+ * also a u32)
+ */
+
+ /* remove the table id from the ordinal */
+ ord &= IPW_ORD_TABLE_VALUE_MASK;
+
+ /* boundary check */
+ if (ord > priv->table1_len) {
+ IPW_DEBUG_ORD("ordinal value too long\n");
+ return -EINVAL;
+ }
+
+ /* verify we have enough room to store the value */
+ if (*len < sizeof(u32)) {
+ IPW_DEBUG_ORD("ordinal buffer length too small, "
+ "need %zd\n", sizeof(u32));
+ return -EINVAL;
+ }
+
+ *((u32 *)val) = ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
+ *len = sizeof(u32);
+ break;
+
+ case IPW_ORD_TABLE_2_MASK:
+ /*
+ * TABLE 2: Indirect access to a table of variable sized values
+ *
+ * This table consist of six values, each containing
+ * - dword containing the starting offset of the data
+ * - dword containing the lengh in the first 16bits
+ * and the count in the second 16bits
+ */
+
+ /* remove the table id from the ordinal */
+ ord &= IPW_ORD_TABLE_VALUE_MASK;
+
+ /* boundary check */
+ if (ord > priv->table2_len) {
+ IPW_DEBUG_ORD("ordinal value too long\n");
+ return -EINVAL;
+ }
+
+ /* get the address of statistic */
+ addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
+
+ /* get the second DW of statistics ;
+ * two 16-bit words - first is length, second is count */
+ field_info = ipw_read_reg32(priv, priv->table2_addr + (ord << 3) + sizeof(u32));
+
+ /* get each entry length */
+ field_len = *((u16 *)&field_info);
+
+ /* get number of entries */
+ field_count = *(((u16 *)&field_info) + 1);
+
+ /* abort if not enought memory */
+ total_len = field_len * field_count;
+ if (total_len > *len) {
+ *len = total_len;
+ return -EINVAL;
+ }
+
+ *len = total_len;
+ if (!total_len)
+ return 0;
+
+ IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
+ "field_info = 0x%08x\n",
+ addr, total_len, field_info);
+ ipw_read_indirect(priv, addr, val, total_len);
+ break;
+
+ default:
+ IPW_DEBUG_ORD("Invalid ordinal!\n");
+ return -EINVAL;
+
+ }
+
+
+ return 0;
+}
+
+static void ipw_init_ordinals(struct ipw_priv *priv)
+{
+ priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
+ priv->table0_len = ipw_read32(priv, priv->table0_addr);
+
+ IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
+ priv->table0_addr, priv->table0_len);
+
+ priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
+ priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
+
+ IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
+ priv->table1_addr, priv->table1_len);
+
+ priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
+ priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
+ priv->table2_len &= 0x0000ffff; /* use first two bytes */
+
+ IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
+ priv->table2_addr, priv->table2_len);
+
+}
+
+/*
+ * The following adds a new attribute to the sysfs representation
+ * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/)
+ * used for controling the debug level.
+ *
+ * See the level definitions in ipw for details.
+ */
+static ssize_t show_debug_level(struct device_driver *d, char *buf)
+{
+ return sprintf(buf, "0x%08X\n", ipw_debug_level);
+}
+static ssize_t store_debug_level(struct device_driver *d,
+ const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+ u32 val;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ p++;
+ if (p[0] == 'x' || p[0] == 'X')
+ p++;
+ val = simple_strtoul(p, &p, 16);
+ } else
+ val = simple_strtoul(p, &p, 10);
+ if (p == buf)
+ printk(KERN_INFO DRV_NAME
+ ": %s is not in hex or decimal form.\n", buf);
+ else
+ ipw_debug_level = val;
+
+ return strnlen(buf, count);
+}
+
+static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
+ show_debug_level, store_debug_level);
+
+static ssize_t show_status(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw_priv *p = d->driver_data;
+ return sprintf(buf, "0x%08x\n", (int)p->status);
+}
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+
+static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw_priv *p = d->driver_data;
+ return sprintf(buf, "0x%08x\n", (int)p->config);
+}
+static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
+
+static ssize_t show_nic_type(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw_priv *p = d->driver_data;
+ u8 type = p->eeprom[EEPROM_NIC_TYPE];
+
+ switch (type) {
+ case EEPROM_NIC_TYPE_STANDARD:
+ return sprintf(buf, "STANDARD\n");
+ case EEPROM_NIC_TYPE_DELL:
+ return sprintf(buf, "DELL\n");
+ case EEPROM_NIC_TYPE_FUJITSU:
+ return sprintf(buf, "FUJITSU\n");
+ case EEPROM_NIC_TYPE_IBM:
+ return sprintf(buf, "IBM\n");
+ case EEPROM_NIC_TYPE_HP:
+ return sprintf(buf, "HP\n");
+ }
+
+ return sprintf(buf, "UNKNOWN\n");
+}
+static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
+
+static ssize_t dump_error_log(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+
+ if (p[0] == '1')
+ ipw_dump_nic_error_log((struct ipw_priv*)d->driver_data);
+
+ return strnlen(buf, count);
+}
+static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, dump_error_log);
+
+static ssize_t dump_event_log(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+
+ if (p[0] == '1')
+ ipw_dump_nic_event_log((struct ipw_priv*)d->driver_data);
+
+ return strnlen(buf, count);
+}
+static DEVICE_ATTR(dump_events, S_IWUSR, NULL, dump_event_log);
+
+static ssize_t show_ucode_version(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u32 len = sizeof(u32), tmp = 0;
+ struct ipw_priv *p = d->driver_data;
+
+ if(ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len))
+ return 0;
+
+ return sprintf(buf, "0x%08x\n", tmp);
+}
+static DEVICE_ATTR(ucode_version, S_IWUSR|S_IRUGO, show_ucode_version, NULL);
+
+static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ u32 len = sizeof(u32), tmp = 0;
+ struct ipw_priv *p = d->driver_data;
+
+ if(ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len))
+ return 0;
+
+ return sprintf(buf, "0x%08x\n", tmp);
+}
+static DEVICE_ATTR(rtc, S_IWUSR|S_IRUGO, show_rtc, NULL);
+
+/*
+ * Add a device attribute to view/control the delay between eeprom
+ * operations.
+ */
+static ssize_t show_eeprom_delay(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ int n = ((struct ipw_priv*)d->driver_data)->eeprom_delay;
+ return sprintf(buf, "%i\n", n);
+}
+static ssize_t store_eeprom_delay(struct device *d,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct ipw_priv *p = d->driver_data;
+ sscanf(buf, "%i", &p->eeprom_delay);
+ return strnlen(buf, count);
+}
+static DEVICE_ATTR(eeprom_delay, S_IWUSR|S_IRUGO,
+ show_eeprom_delay,store_eeprom_delay);
+
+static ssize_t show_command_event_reg(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u32 reg = 0;
+ struct ipw_priv *p = d->driver_data;
+
+ reg = ipw_read_reg32(p, CX2_INTERNAL_CMD_EVENT);
+ return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_command_event_reg(struct device *d,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ u32 reg;
+ struct ipw_priv *p = d->driver_data;
+
+ sscanf(buf, "%x", &reg);
+ ipw_write_reg32(p, CX2_INTERNAL_CMD_EVENT, reg);
+ return strnlen(buf, count);
+}
+static DEVICE_ATTR(command_event_reg, S_IWUSR|S_IRUGO,
+ show_command_event_reg,store_command_event_reg);
+
+static ssize_t show_mem_gpio_reg(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u32 reg = 0;
+ struct ipw_priv *p = d->driver_data;
+
+ reg = ipw_read_reg32(p, 0x301100);
+ return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_mem_gpio_reg(struct device *d,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ u32 reg;
+ struct ipw_priv *p = d->driver_data;
+
+ sscanf(buf, "%x", &reg);
+ ipw_write_reg32(p, 0x301100, reg);
+ return strnlen(buf, count);
+}
+static DEVICE_ATTR(mem_gpio_reg, S_IWUSR|S_IRUGO,
+ show_mem_gpio_reg,store_mem_gpio_reg);
+
+static ssize_t show_indirect_dword(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u32 reg = 0;
+ struct ipw_priv *priv = d->driver_data;
+ if (priv->status & STATUS_INDIRECT_DWORD)
+ reg = ipw_read_reg32(priv, priv->indirect_dword);
+ else
+ reg = 0;
+
+ return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_indirect_dword(struct device *d,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct ipw_priv *priv = d->driver_data;
+
+ sscanf(buf, "%x", &priv->indirect_dword);
+ priv->status |= STATUS_INDIRECT_DWORD;
+ return strnlen(buf, count);
+}
+static DEVICE_ATTR(indirect_dword, S_IWUSR|S_IRUGO,
+ show_indirect_dword,store_indirect_dword);
+
+static ssize_t show_indirect_byte(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u8 reg = 0;
+ struct ipw_priv *priv = d->driver_data;
+ if (priv->status & STATUS_INDIRECT_BYTE)
+ reg = ipw_read_reg8(priv, priv->indirect_byte);
+ else
+ reg = 0;
+
+ return sprintf(buf, "0x%02x\n", reg);
+}
+static ssize_t store_indirect_byte(struct device *d,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct ipw_priv *priv = d->driver_data;
+
+ sscanf(buf, "%x", &priv->indirect_byte);
+ priv->status |= STATUS_INDIRECT_BYTE;
+ return strnlen(buf, count);
+}
+static DEVICE_ATTR(indirect_byte, S_IWUSR|S_IRUGO,
+ show_indirect_byte, store_indirect_byte);
+
+static ssize_t show_direct_dword(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u32 reg = 0;
+ struct ipw_priv *priv = d->driver_data;
+
+ if (priv->status & STATUS_DIRECT_DWORD)
+ reg = ipw_read32(priv, priv->direct_dword);
+ else
+ reg = 0;
+
+ return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_direct_dword(struct device *d,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct ipw_priv *priv = d->driver_data;
+
+ sscanf(buf, "%x", &priv->direct_dword);
+ priv->status |= STATUS_DIRECT_DWORD;
+ return strnlen(buf, count);
+}
+static DEVICE_ATTR(direct_dword, S_IWUSR|S_IRUGO,
+ show_direct_dword,store_direct_dword);
+
+
+static inline int rf_kill_active(struct ipw_priv *priv)
+{
+ if (0 == (ipw_read32(priv, 0x30) & 0x10000))
+ priv->status |= STATUS_RF_KILL_HW;
+ else
+ priv->status &= ~STATUS_RF_KILL_HW;
+
+ return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
+}
+
+static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ /* 0 - RF kill not enabled
+ 1 - SW based RF kill active (sysfs)
+ 2 - HW based RF kill active
+ 3 - Both HW and SW baed RF kill active */
+ struct ipw_priv *priv = d->driver_data;
+ int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
+ (rf_kill_active(priv) ? 0x2 : 0x0);
+ return sprintf(buf, "%i\n", val);
+}
+
+static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
+{
+ if ((disable_radio ? 1 : 0) ==
+ (priv->status & STATUS_RF_KILL_SW ? 1 : 0))
+ return 0 ;
+
+ IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
+ disable_radio ? "OFF" : "ON");
+
+ if (disable_radio) {
+ priv->status |= STATUS_RF_KILL_SW;
+
+ if (priv->workqueue) {
+ cancel_delayed_work(&priv->request_scan);
+ }
+ wake_up_interruptible(&priv->wait_command_queue);
+ queue_work(priv->workqueue, &priv->down);
+ } else {
+ priv->status &= ~STATUS_RF_KILL_SW;
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_RF_KILL("Can not turn radio back on - "
+ "disabled by HW switch\n");
+ /* Make sure the RF_KILL check timer is running */
+ cancel_delayed_work(&priv->rf_kill);
+ queue_delayed_work(priv->workqueue, &priv->rf_kill,
+ 2 * HZ);
+ } else
+ queue_work(priv->workqueue, &priv->up);
+ }
+
+ return 1;
+}
+
+static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = d->driver_data;
+
+ ipw_radio_kill_sw(priv, buf[0] == '1');
+
+ return count;
+}
+static DEVICE_ATTR(rf_kill, S_IWUSR|S_IRUGO, show_rf_kill, store_rf_kill);
+
+static void ipw_irq_tasklet(struct ipw_priv *priv)
+{
+ u32 inta, inta_mask, handled = 0;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ inta = ipw_read32(priv, CX2_INTA_RW);
+ inta_mask = ipw_read32(priv, CX2_INTA_MASK_R);
+ inta &= (CX2_INTA_MASK_ALL & inta_mask);
+
+ /* Add any cached INTA values that need to be handled */
+ inta |= priv->isr_inta;
+
+ /* handle all the justifications for the interrupt */
+ if (inta & CX2_INTA_BIT_RX_TRANSFER) {
+ ipw_rx(priv);
+ handled |= CX2_INTA_BIT_RX_TRANSFER;
+ }
+
+ if (inta & CX2_INTA_BIT_TX_CMD_QUEUE) {
+ IPW_DEBUG_HC("Command completed.\n");
+ rc = ipw_queue_tx_reclaim( priv, &priv->txq_cmd, -1);
+ priv->status &= ~STATUS_HCMD_ACTIVE;
+ wake_up_interruptible(&priv->wait_command_queue);
+ handled |= CX2_INTA_BIT_TX_CMD_QUEUE;
+ }
+
+ if (inta & CX2_INTA_BIT_TX_QUEUE_1) {
+ IPW_DEBUG_TX("TX_QUEUE_1\n");
+ rc = ipw_queue_tx_reclaim( priv, &priv->txq[0], 0);
+ handled |= CX2_INTA_BIT_TX_QUEUE_1;
+ }
+
+ if (inta & CX2_INTA_BIT_TX_QUEUE_2) {
+ IPW_DEBUG_TX("TX_QUEUE_2\n");
+ rc = ipw_queue_tx_reclaim( priv, &priv->txq[1], 1);
+ handled |= CX2_INTA_BIT_TX_QUEUE_2;
+ }
+
+ if (inta & CX2_INTA_BIT_TX_QUEUE_3) {
+ IPW_DEBUG_TX("TX_QUEUE_3\n");
+ rc = ipw_queue_tx_reclaim( priv, &priv->txq[2], 2);
+ handled |= CX2_INTA_BIT_TX_QUEUE_3;
+ }
+
+ if (inta & CX2_INTA_BIT_TX_QUEUE_4) {
+ IPW_DEBUG_TX("TX_QUEUE_4\n");
+ rc = ipw_queue_tx_reclaim( priv, &priv->txq[3], 3);
+ handled |= CX2_INTA_BIT_TX_QUEUE_4;
+ }
+
+ if (inta & CX2_INTA_BIT_STATUS_CHANGE) {
+ IPW_WARNING("STATUS_CHANGE\n");
+ handled |= CX2_INTA_BIT_STATUS_CHANGE;
+ }
+
+ if (inta & CX2_INTA_BIT_BEACON_PERIOD_EXPIRED) {
+ IPW_WARNING("TX_PERIOD_EXPIRED\n");
+ handled |= CX2_INTA_BIT_BEACON_PERIOD_EXPIRED;
+ }
+
+ if (inta & CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) {
+ IPW_WARNING("HOST_CMD_DONE\n");
+ handled |= CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE;
+ }
+
+ if (inta & CX2_INTA_BIT_FW_INITIALIZATION_DONE) {
+ IPW_WARNING("FW_INITIALIZATION_DONE\n");
+ handled |= CX2_INTA_BIT_FW_INITIALIZATION_DONE;
+ }
+
+ if (inta & CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) {
+ IPW_WARNING("PHY_OFF_DONE\n");
+ handled |= CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE;
+ }
+
+ if (inta & CX2_INTA_BIT_RF_KILL_DONE) {
+ IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
+ priv->status |= STATUS_RF_KILL_HW;
+ wake_up_interruptible(&priv->wait_command_queue);
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+ cancel_delayed_work(&priv->request_scan);
+ queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
+ handled |= CX2_INTA_BIT_RF_KILL_DONE;
+ }
+
+ if (inta & CX2_INTA_BIT_FATAL_ERROR) {
+ IPW_ERROR("Firmware error detected. Restarting.\n");
+#ifdef CONFIG_IPW_DEBUG
+ if (ipw_debug_level & IPW_DL_FW_ERRORS) {
+ ipw_dump_nic_error_log(priv);
+ ipw_dump_nic_event_log(priv);
+ }
+#endif
+ queue_work(priv->workqueue, &priv->adapter_restart);
+ handled |= CX2_INTA_BIT_FATAL_ERROR;
+ }
+
+ if (inta & CX2_INTA_BIT_PARITY_ERROR) {
+ IPW_ERROR("Parity error\n");
+ handled |= CX2_INTA_BIT_PARITY_ERROR;
+ }
+
+ if (handled != inta) {
+ IPW_ERROR("Unhandled INTA bits 0x%08x\n",
+ inta & ~handled);
+ }
+
+ /* enable all interrupts */
+ ipw_enable_interrupts(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+#ifdef CONFIG_IPW_DEBUG
+#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
+static char *get_cmd_string(u8 cmd)
+{
+ switch (cmd) {
+ IPW_CMD(HOST_COMPLETE);
+ IPW_CMD(POWER_DOWN);
+ IPW_CMD(SYSTEM_CONFIG);
+ IPW_CMD(MULTICAST_ADDRESS);
+ IPW_CMD(SSID);
+ IPW_CMD(ADAPTER_ADDRESS);
+ IPW_CMD(PORT_TYPE);
+ IPW_CMD(RTS_THRESHOLD);
+ IPW_CMD(FRAG_THRESHOLD);
+ IPW_CMD(POWER_MODE);
+ IPW_CMD(WEP_KEY);
+ IPW_CMD(TGI_TX_KEY);
+ IPW_CMD(SCAN_REQUEST);
+ IPW_CMD(SCAN_REQUEST_EXT);
+ IPW_CMD(ASSOCIATE);
+ IPW_CMD(SUPPORTED_RATES);
+ IPW_CMD(SCAN_ABORT);
+ IPW_CMD(TX_FLUSH);
+ IPW_CMD(QOS_PARAMETERS);
+ IPW_CMD(DINO_CONFIG);
+ IPW_CMD(RSN_CAPABILITIES);
+ IPW_CMD(RX_KEY);
+ IPW_CMD(CARD_DISABLE);
+ IPW_CMD(SEED_NUMBER);
+ IPW_CMD(TX_POWER);
+ IPW_CMD(COUNTRY_INFO);
+ IPW_CMD(AIRONET_INFO);
+ IPW_CMD(AP_TX_POWER);
+ IPW_CMD(CCKM_INFO);
+ IPW_CMD(CCX_VER_INFO);
+ IPW_CMD(SET_CALIBRATION);
+ IPW_CMD(SENSITIVITY_CALIB);
+ IPW_CMD(RETRY_LIMIT);
+ IPW_CMD(IPW_PRE_POWER_DOWN);
+ IPW_CMD(VAP_BEACON_TEMPLATE);
+ IPW_CMD(VAP_DTIM_PERIOD);
+ IPW_CMD(EXT_SUPPORTED_RATES);
+ IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT);
+ IPW_CMD(VAP_QUIET_INTERVALS);
+ IPW_CMD(VAP_CHANNEL_SWITCH);
+ IPW_CMD(VAP_MANDATORY_CHANNELS);
+ IPW_CMD(VAP_CELL_PWR_LIMIT);
+ IPW_CMD(VAP_CF_PARAM_SET);
+ IPW_CMD(VAP_SET_BEACONING_STATE);
+ IPW_CMD(MEASUREMENT);
+ IPW_CMD(POWER_CAPABILITY);
+ IPW_CMD(SUPPORTED_CHANNELS);
+ IPW_CMD(TPC_REPORT);
+ IPW_CMD(WME_INFO);
+ IPW_CMD(PRODUCTION_COMMAND);
+ default:
+ return "UNKNOWN";
+ }
+}
+#endif /* CONFIG_IPW_DEBUG */
+
+#define HOST_COMPLETE_TIMEOUT HZ
+static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
+{
+ int rc = 0;
+
+ if (priv->status & STATUS_HCMD_ACTIVE) {
+ IPW_ERROR("Already sending a command\n");
+ return -1;
+ }
+
+ priv->status |= STATUS_HCMD_ACTIVE;
+
+ IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n",
+ get_cmd_string(cmd->cmd), cmd->cmd, cmd->len);
+ printk_buf(IPW_DL_HOST_COMMAND, (u8*)cmd->param, cmd->len);
+
+ rc = ipw_queue_tx_hcmd(priv, cmd->cmd, &cmd->param, cmd->len, 0);
+ if (rc)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(
+ priv->wait_command_queue, !(priv->status & STATUS_HCMD_ACTIVE),
+ HOST_COMPLETE_TIMEOUT);
+ if (rc == 0) {
+ IPW_DEBUG_INFO("Command completion failed out after %dms.\n",
+ jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
+ priv->status &= ~STATUS_HCMD_ACTIVE;
+ return -EIO;
+ }
+ if (priv->status & STATUS_RF_KILL_MASK) {
+ IPW_DEBUG_INFO("Command aborted due to RF Kill Switch\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int ipw_send_host_complete(struct ipw_priv *priv)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_HOST_COMPLETE,
+ .len = 0
+ };
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send HOST_COMPLETE command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipw_send_system_config(struct ipw_priv *priv,
+ struct ipw_sys_config *config)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_SYSTEM_CONFIG,
+ .len = sizeof(*config)
+ };
+
+ if (!priv || !config) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ memcpy(&cmd.param,config,sizeof(*config));
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send SYSTEM_CONFIG command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipw_send_ssid(struct ipw_priv *priv, u8 *ssid, int len)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_SSID,
+ .len = min(len, IW_ESSID_MAX_SIZE)
+ };
+
+ if (!priv || !ssid) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ memcpy(&cmd.param, ssid, cmd.len);
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send SSID command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipw_send_adapter_address(struct ipw_priv *priv, u8 *mac)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_ADAPTER_ADDRESS,
+ .len = ETH_ALEN
+ };
+
+ if (!priv || !mac) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ IPW_DEBUG_INFO("%s: Setting MAC to " MAC_FMT "\n",
+ priv->net_dev->name, MAC_ARG(mac));
+
+ memcpy(&cmd.param, mac, ETH_ALEN);
+
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send ADAPTER_ADDRESS command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ipw_adapter_restart(void *adapter)
+{
+ struct ipw_priv *priv = adapter;
+
+ if (priv->status & STATUS_RF_KILL_MASK)
+ return;
+
+ ipw_down(priv);
+ if (ipw_up(priv)) {
+ IPW_ERROR("Failed to up device\n");
+ return;
+ }
+}
+
+
+
+
+#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ)
+
+static void ipw_scan_check(void *data)
+{
+ struct ipw_priv *priv = data;
+ if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
+ IPW_DEBUG_SCAN("Scan completion watchdog resetting "
+ "adapter (%dms).\n",
+ IPW_SCAN_CHECK_WATCHDOG / 100);
+ ipw_adapter_restart(priv);
+ }
+}
+
+static int ipw_send_scan_request_ext(struct ipw_priv *priv,
+ struct ipw_scan_request_ext *request)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_SCAN_REQUEST_EXT,
+ .len = sizeof(*request)
+ };
+
+ if (!priv || !request) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ memcpy(&cmd.param,request,sizeof(*request));
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send SCAN_REQUEST_EXT command\n");
+ return -1;
+ }
+
+ queue_delayed_work(priv->workqueue, &priv->scan_check,
+ IPW_SCAN_CHECK_WATCHDOG);
+ return 0;
+}
+
+static int ipw_send_scan_abort(struct ipw_priv *priv)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_SCAN_ABORT,
+ .len = 0
+ };
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send SCAN_ABORT command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_SENSITIVITY_CALIB,
+ .len = sizeof(struct ipw_sensitivity_calib)
+ };
+ struct ipw_sensitivity_calib *calib = (struct ipw_sensitivity_calib *)
+ &cmd.param;
+ calib->beacon_rssi_raw = sens;
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send SENSITIVITY CALIB command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipw_send_associate(struct ipw_priv *priv,
+ struct ipw_associate *associate)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_ASSOCIATE,
+ .len = sizeof(*associate)
+ };
+
+ if (!priv || !associate) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ memcpy(&cmd.param,associate,sizeof(*associate));
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send ASSOCIATE command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipw_send_supported_rates(struct ipw_priv *priv,
+ struct ipw_supported_rates *rates)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_SUPPORTED_RATES,
+ .len = sizeof(*rates)
+ };
+
+ if (!priv || !rates) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ memcpy(&cmd.param,rates,sizeof(*rates));
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send SUPPORTED_RATES command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipw_set_random_seed(struct ipw_priv *priv)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_SEED_NUMBER,
+ .len = sizeof(u32)
+ };
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ get_random_bytes(&cmd.param, sizeof(u32));
+
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send SEED_NUMBER command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+#if 0
+static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_CARD_DISABLE,
+ .len = sizeof(u32)
+ };
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ *((u32*)&cmd.param) = phy_off;
+
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send CARD_DISABLE command\n");
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+static int ipw_send_tx_power(struct ipw_priv *priv,
+ struct ipw_tx_power *power)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_TX_POWER,
+ .len = sizeof(*power)
+ };
+
+ if (!priv || !power) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ memcpy(&cmd.param,power,sizeof(*power));
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send TX_POWER command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts)
+{
+ struct ipw_rts_threshold rts_threshold = {
+ .rts_threshold = rts,
+ };
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_RTS_THRESHOLD,
+ .len = sizeof(rts_threshold)
+ };
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ memcpy(&cmd.param, &rts_threshold, sizeof(rts_threshold));
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send RTS_THRESHOLD command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
+{
+ struct ipw_frag_threshold frag_threshold = {
+ .frag_threshold = frag,
+ };
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_FRAG_THRESHOLD,
+ .len = sizeof(frag_threshold)
+ };
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ memcpy(&cmd.param, &frag_threshold, sizeof(frag_threshold));
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send FRAG_THRESHOLD command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
+{
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_POWER_MODE,
+ .len = sizeof(u32)
+ };
+ u32 *param = (u32*)(&cmd.param);
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ /* If on battery, set to 3, if AC set to CAM, else user
+ * level */
+ switch (mode) {
+ case IPW_POWER_BATTERY:
+ *param = IPW_POWER_INDEX_3;
+ break;
+ case IPW_POWER_AC:
+ *param = IPW_POWER_MODE_CAM;
+ break;
+ default:
+ *param = mode;
+ break;
+ }
+
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send POWER_MODE command\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * The IPW device contains a Microwire compatible EEPROM that stores
+ * various data like the MAC address. Usually the firmware has exclusive
+ * access to the eeprom, but during device initialization (before the
+ * device driver has sent the HostComplete command to the firmware) the
+ * device driver has read access to the EEPROM by way of indirect addressing
+ * through a couple of memory mapped registers.
+ *
+ * The following is a simplified implementation for pulling data out of the
+ * the eeprom, along with some helper functions to find information in
+ * the per device private data's copy of the eeprom.
+ *
+ * NOTE: To better understand how these functions work (i.e what is a chip
+ * select and why do have to keep driving the eeprom clock?), read
+ * just about any data sheet for a Microwire compatible EEPROM.
+ */
+
+/* write a 32 bit value into the indirect accessor register */
+static inline void eeprom_write_reg(struct ipw_priv *p, u32 data)
+{
+ ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
+
+ /* the eeprom requires some time to complete the operation */
+ udelay(p->eeprom_delay);
+
+ return;
+}
+
+/* perform a chip select operation */
+static inline void eeprom_cs(struct ipw_priv* priv)
+{
+ eeprom_write_reg(priv,0);
+ eeprom_write_reg(priv,EEPROM_BIT_CS);
+ eeprom_write_reg(priv,EEPROM_BIT_CS|EEPROM_BIT_SK);
+ eeprom_write_reg(priv,EEPROM_BIT_CS);
+}
+
+/* perform a chip select operation */
+static inline void eeprom_disable_cs(struct ipw_priv* priv)
+{
+ eeprom_write_reg(priv,EEPROM_BIT_CS);
+ eeprom_write_reg(priv,0);
+ eeprom_write_reg(priv,EEPROM_BIT_SK);
+}
+
+/* push a single bit down to the eeprom */
+static inline void eeprom_write_bit(struct ipw_priv *p,u8 bit)
+{
+ int d = ( bit ? EEPROM_BIT_DI : 0);
+ eeprom_write_reg(p,EEPROM_BIT_CS|d);
+ eeprom_write_reg(p,EEPROM_BIT_CS|d|EEPROM_BIT_SK);
+}
+
+/* push an opcode followed by an address down to the eeprom */
+static void eeprom_op(struct ipw_priv* priv, u8 op, u8 addr)
+{
+ int i;
+
+ eeprom_cs(priv);
+ eeprom_write_bit(priv,1);
+ eeprom_write_bit(priv,op&2);
+ eeprom_write_bit(priv,op&1);
+ for ( i=7; i>=0; i-- ) {
+ eeprom_write_bit(priv,addr&(1<<i));
+ }
+}
+
+/* pull 16 bits off the eeprom, one bit at a time */
+static u16 eeprom_read_u16(struct ipw_priv* priv, u8 addr)
+{
+ int i;
+ u16 r=0;
+
+ /* Send READ Opcode */
+ eeprom_op(priv,EEPROM_CMD_READ,addr);
+
+ /* Send dummy bit */
+ eeprom_write_reg(priv,EEPROM_BIT_CS);
+
+ /* Read the byte off the eeprom one bit at a time */
+ for ( i=0; i<16; i++ ) {
+ u32 data = 0;
+ eeprom_write_reg(priv,EEPROM_BIT_CS|EEPROM_BIT_SK);
+ eeprom_write_reg(priv,EEPROM_BIT_CS);
+ data = ipw_read_reg32(priv,FW_MEM_REG_EEPROM_ACCESS);
+ r = (r<<1) | ((data & EEPROM_BIT_DO)?1:0);
+ }
+
+ /* Send another dummy bit */
+ eeprom_write_reg(priv,0);
+ eeprom_disable_cs(priv);
+
+ return r;
+}
+
+/* helper function for pulling the mac address out of the private */
+/* data's copy of the eeprom data */
+static void eeprom_parse_mac(struct ipw_priv* priv, u8* mac)
+{
+ u8* ee = (u8*)priv->eeprom;
+ memcpy(mac, &ee[EEPROM_MAC_ADDRESS], 6);
+}
+
+/*
+ * Either the device driver (i.e. the host) or the firmware can
+ * load eeprom data into the designated region in SRAM. If neither
+ * happens then the FW will shutdown with a fatal error.
+ *
+ * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE
+ * bit needs region of shared SRAM needs to be non-zero.
+ */
+static void ipw_eeprom_init_sram(struct ipw_priv *priv)
+{
+ int i;
+ u16 *eeprom = (u16 *)priv->eeprom;
+
+ IPW_DEBUG_TRACE(">>\n");
+
+ /* read entire contents of eeprom into private buffer */
+ for ( i=0; i<128; i++ )
+ eeprom[i] = eeprom_read_u16(priv,(u8)i);
+
+ /*
+ If the data looks correct, then copy it to our private
+ copy. Otherwise let the firmware know to perform the operation
+ on it's own
+ */
+ if ((priv->eeprom + EEPROM_VERSION) != 0) {
+ IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
+
+ /* write the eeprom data to sram */
+ for( i=0; i<CX2_EEPROM_IMAGE_SIZE; i++ )
+ ipw_write8(priv, IPW_EEPROM_DATA + i,
+ priv->eeprom[i]);
+
+ /* Do not load eeprom data on fatal error or suspend */
+ ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
+ } else {
+ IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n");
+
+ /* Load eeprom data on fatal error or suspend */
+ ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
+ }
+
+ IPW_DEBUG_TRACE("<<\n");
+}
+
+
+static inline void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count)
+{
+ count >>= 2;
+ if (!count) return;
+ _ipw_write32(priv, CX2_AUTOINC_ADDR, start);
+ while (count--)
+ _ipw_write32(priv, CX2_AUTOINC_DATA, 0);
+}
+
+static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv)
+{
+ ipw_zero_memory(priv, CX2_SHARED_SRAM_DMA_CONTROL,
+ CB_NUMBER_OF_ELEMENTS_SMALL *
+ sizeof(struct command_block));
+}
+
+static int ipw_fw_dma_enable(struct ipw_priv *priv)
+{ /* start dma engine but no transfers yet*/
+
+ IPW_DEBUG_FW(">> : \n");
+
+ /* Start the dma */
+ ipw_fw_dma_reset_command_blocks(priv);
+
+ /* Write CB base address */
+ ipw_write_reg32(priv, CX2_DMA_I_CB_BASE, CX2_SHARED_SRAM_DMA_CONTROL);
+
+ IPW_DEBUG_FW("<< : \n");
+ return 0;
+}
+
+static void ipw_fw_dma_abort(struct ipw_priv *priv)
+{
+ u32 control = 0;
+
+ IPW_DEBUG_FW(">> :\n");
+
+ //set the Stop and Abort bit
+ control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
+ ipw_write_reg32(priv, CX2_DMA_I_DMA_CONTROL, control);
+ priv->sram_desc.last_cb_index = 0;
+
+ IPW_DEBUG_FW("<< \n");
+}
+
+static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index, struct command_block *cb)
+{
+ u32 address = CX2_SHARED_SRAM_DMA_CONTROL + (sizeof(struct command_block) * index);
+ IPW_DEBUG_FW(">> :\n");
+
+ ipw_write_indirect(priv, address, (u8*)cb, (int)sizeof(struct command_block));
+
+ IPW_DEBUG_FW("<< :\n");
+ return 0;
+
+}
+
+static int ipw_fw_dma_kick(struct ipw_priv *priv)
+{
+ u32 control = 0;
+ u32 index=0;
+
+ IPW_DEBUG_FW(">> :\n");
+
+ for (index = 0; index < priv->sram_desc.last_cb_index; index++)
+ ipw_fw_dma_write_command_block(priv, index, &priv->sram_desc.cb_list[index]);
+
+ /* Enable the DMA in the CSR register */
+ ipw_clear_bit(priv, CX2_RESET_REG,CX2_RESET_REG_MASTER_DISABLED | CX2_RESET_REG_STOP_MASTER);
+
+ /* Set the Start bit. */
+ control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START;
+ ipw_write_reg32(priv, CX2_DMA_I_DMA_CONTROL, control);
+
+ IPW_DEBUG_FW("<< :\n");
+ return 0;
+}
+
+static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv)
+{
+ u32 address;
+ u32 register_value=0;
+ u32 cb_fields_address=0;
+
+ IPW_DEBUG_FW(">> :\n");
+ address = ipw_read_reg32(priv,CX2_DMA_I_CURRENT_CB);
+ IPW_DEBUG_FW_INFO("Current CB is 0x%x \n",address);
+
+ /* Read the DMA Controlor register */
+ register_value = ipw_read_reg32(priv, CX2_DMA_I_DMA_CONTROL);
+ IPW_DEBUG_FW_INFO("CX2_DMA_I_DMA_CONTROL is 0x%x \n",register_value);
+
+ /* Print the CB values*/
+ cb_fields_address = address;
+ register_value = ipw_read_reg32(priv, cb_fields_address);
+ IPW_DEBUG_FW_INFO("Current CB ControlField is 0x%x \n",register_value);
+
+ cb_fields_address += sizeof(u32);
+ register_value = ipw_read_reg32(priv, cb_fields_address);
+ IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x \n",register_value);
+
+ cb_fields_address += sizeof(u32);
+ register_value = ipw_read_reg32(priv, cb_fields_address);
+ IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x \n",
+ register_value);
+
+ cb_fields_address += sizeof(u32);
+ register_value = ipw_read_reg32(priv, cb_fields_address);
+ IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x \n",register_value);
+
+ IPW_DEBUG_FW(">> :\n");
+}
+
+static int ipw_fw_dma_command_block_index(struct ipw_priv *priv)
+{
+ u32 current_cb_address = 0;
+ u32 current_cb_index = 0;
+
+ IPW_DEBUG_FW("<< :\n");
+ current_cb_address= ipw_read_reg32(priv, CX2_DMA_I_CURRENT_CB);
+
+ current_cb_index = (current_cb_address - CX2_SHARED_SRAM_DMA_CONTROL )/
+ sizeof (struct command_block);
+
+ IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X \n",
+ current_cb_index, current_cb_address );
+
+ IPW_DEBUG_FW(">> :\n");
+ return current_cb_index;
+
+}
+
+static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
+ u32 src_address,
+ u32 dest_address,
+ u32 length,
+ int interrupt_enabled,
+ int is_last)
+{
+
+ u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC |
+ CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG |
+ CB_DEST_SIZE_LONG;
+ struct command_block *cb;
+ u32 last_cb_element=0;
+
+ IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n",
+ src_address, dest_address, length);
+
+ if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL)
+ return -1;
+
+ last_cb_element = priv->sram_desc.last_cb_index;
+ cb = &priv->sram_desc.cb_list[last_cb_element];
+ priv->sram_desc.last_cb_index++;
+
+ /* Calculate the new CB control word */
+ if (interrupt_enabled )
+ control |= CB_INT_ENABLED;
+
+ if (is_last)
+ control |= CB_LAST_VALID;
+
+ control |= length;
+
+ /* Calculate the CB Element's checksum value */
+ cb->status = control ^src_address ^dest_address;
+
+ /* Copy the Source and Destination addresses */
+ cb->dest_addr = dest_address;
+ cb->source_addr = src_address;
+
+ /* Copy the Control Word last */
+ cb->control = control;
+
+ return 0;
+}
+
+static int ipw_fw_dma_add_buffer(struct ipw_priv *priv,
+ u32 src_phys,
+ u32 dest_address,
+ u32 length)
+{
+ u32 bytes_left = length;
+ u32 src_offset=0;
+ u32 dest_offset=0;
+ int status = 0;
+ IPW_DEBUG_FW(">> \n");
+ IPW_DEBUG_FW_INFO("src_phys=0x%x dest_address=0x%x length=0x%x\n",
+ src_phys, dest_address, length);
+ while (bytes_left > CB_MAX_LENGTH) {
+ status = ipw_fw_dma_add_command_block( priv,
+ src_phys + src_offset,
+ dest_address + dest_offset,
+ CB_MAX_LENGTH, 0, 0);
+ if (status) {
+ IPW_DEBUG_FW_INFO(": Failed\n");
+ return -1;
+ } else
+ IPW_DEBUG_FW_INFO(": Added new cb\n");
+
+ src_offset += CB_MAX_LENGTH;
+ dest_offset += CB_MAX_LENGTH;
+ bytes_left -= CB_MAX_LENGTH;
+ }
+
+ /* add the buffer tail */
+ if (bytes_left > 0) {
+ status = ipw_fw_dma_add_command_block(
+ priv, src_phys + src_offset,
+ dest_address + dest_offset,
+ bytes_left, 0, 0);
+ if (status) {
+ IPW_DEBUG_FW_INFO(": Failed on the buffer tail\n");
+ return -1;
+ } else
+ IPW_DEBUG_FW_INFO(": Adding new cb - the buffer tail\n");
+ }
+
+
+ IPW_DEBUG_FW("<< \n");
+ return 0;
+}
+
+static int ipw_fw_dma_wait(struct ipw_priv *priv)
+{
+ u32 current_index = 0;
+ u32 watchdog = 0;
+
+ IPW_DEBUG_FW(">> : \n");
+
+ current_index = ipw_fw_dma_command_block_index(priv);
+ IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%8X\n",
+ (int) priv->sram_desc.last_cb_index);
+
+ while (current_index < priv->sram_desc.last_cb_index) {
+ udelay(50);
+ current_index = ipw_fw_dma_command_block_index(priv);
+
+ watchdog++;
+
+ if (watchdog > 400) {
+ IPW_DEBUG_FW_INFO("Timeout\n");
+ ipw_fw_dma_dump_command_block(priv);
+ ipw_fw_dma_abort(priv);
+ return -1;
+ }
+ }
+
+ ipw_fw_dma_abort(priv);
+
+ /*Disable the DMA in the CSR register*/
+ ipw_set_bit(priv, CX2_RESET_REG,
+ CX2_RESET_REG_MASTER_DISABLED | CX2_RESET_REG_STOP_MASTER);
+
+ IPW_DEBUG_FW("<< dmaWaitSync \n");
+ return 0;
+}
+
+static void ipw_remove_current_network(struct ipw_priv *priv)
+{
+ struct list_head *element, *safe;
+ struct ieee80211_network *network = NULL;
+ list_for_each_safe(element, safe, &priv->ieee->network_list) {
+ network = list_entry(element, struct ieee80211_network, list);
+ if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
+ list_del(element);
+ list_add_tail(&network->list,
+ &priv->ieee->network_free_list);
+ }
+ }
+}
+
+/**
+ * Check that card is still alive.
+ * Reads debug register from domain0.
+ * If card is present, pre-defined value should
+ * be found there.
+ *
+ * @param priv
+ * @return 1 if card is present, 0 otherwise
+ */
+static inline int ipw_alive(struct ipw_priv *priv)
+{
+ return ipw_read32(priv, 0x90) == 0xd55555d5;
+}
+
+static inline int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
+ int timeout)
+{
+ int i = 0;
+
+ do {
+ if ((ipw_read32(priv, addr) & mask) == mask)
+ return i;
+ mdelay(10);
+ i += 10;
+ } while (i < timeout);
+
+ return -ETIME;
+}
+
+/* These functions load the firmware and micro code for the operation of
+ * the ipw hardware. It assumes the buffer has all the bits for the
+ * image and the caller is handling the memory allocation and clean up.
+ */
+
+
+static int ipw_stop_master(struct ipw_priv * priv)
+{
+ int rc;
+
+ IPW_DEBUG_TRACE(">> \n");
+ /* stop master. typical delay - 0 */
+ ipw_set_bit(priv, CX2_RESET_REG, CX2_RESET_REG_STOP_MASTER);
+
+ rc = ipw_poll_bit(priv, CX2_RESET_REG,
+ CX2_RESET_REG_MASTER_DISABLED, 100);
+ if (rc < 0) {
+ IPW_ERROR("stop master failed in 10ms\n");
+ return -1;
+ }
+
+ IPW_DEBUG_INFO("stop master %dms\n", rc);
+
+ return rc;
+}
+
+static void ipw_arc_release(struct ipw_priv *priv)
+{
+ IPW_DEBUG_TRACE(">> \n");
+ mdelay(5);
+
+ ipw_clear_bit(priv, CX2_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
+
+ /* no one knows timing, for safety add some delay */
+ mdelay(5);
+}
+
+struct fw_header {
+ u32 version;
+ u32 mode;
+};
+
+struct fw_chunk {
+ u32 address;
+ u32 length;
+};
+
+#define IPW_FW_MAJOR_VERSION 2
+#define IPW_FW_MINOR_VERSION 2
+
+#define IPW_FW_MINOR(x) ((x & 0xff) >> 8)
+#define IPW_FW_MAJOR(x) (x & 0xff)
+
+#define IPW_FW_VERSION ((IPW_FW_MINOR_VERSION << 8) | \
+ IPW_FW_MAJOR_VERSION)
+
+#define IPW_FW_PREFIX "ipw-" __stringify(IPW_FW_MAJOR_VERSION) \
+"." __stringify(IPW_FW_MINOR_VERSION) "-"
+
+#if IPW_FW_MAJOR_VERSION >= 2 && IPW_FW_MINOR_VERSION > 0
+#define IPW_FW_NAME(x) IPW_FW_PREFIX "" x ".fw"
+#else
+#define IPW_FW_NAME(x) "ipw2200_" x ".fw"
+#endif
+
+static int ipw_load_ucode(struct ipw_priv *priv, u8 * data,
+ size_t len)
+{
+ int rc = 0, i, addr;
+ u8 cr = 0;
+ u16 *image;
+
+ image = (u16 *)data;
+
+ IPW_DEBUG_TRACE(">> \n");
+
+ rc = ipw_stop_master(priv);
+
+ if (rc < 0)
+ return rc;
+
+// spin_lock_irqsave(&priv->lock, flags);
+
+ for (addr = CX2_SHARED_LOWER_BOUND;
+ addr < CX2_REGISTER_DOMAIN1_END; addr += 4) {
+ ipw_write32(priv, addr, 0);
+ }
+
+ /* no ucode (yet) */
+ memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
+ /* destroy DMA queues */
+ /* reset sequence */
+
+ ipw_write_reg32(priv, CX2_MEM_HALT_AND_RESET ,CX2_BIT_HALT_RESET_ON);
+ ipw_arc_release(priv);
+ ipw_write_reg32(priv, CX2_MEM_HALT_AND_RESET, CX2_BIT_HALT_RESET_OFF);
+ mdelay(1);
+
+ /* reset PHY */
+ ipw_write_reg32(priv, CX2_INTERNAL_CMD_EVENT, CX2_BASEBAND_POWER_DOWN);
+ mdelay(1);
+
+ ipw_write_reg32(priv, CX2_INTERNAL_CMD_EVENT, 0);
+ mdelay(1);
+
+ /* enable ucode store */
+ ipw_write_reg8(priv, DINO_CONTROL_REG, 0x0);
+ ipw_write_reg8(priv, DINO_CONTROL_REG, DINO_ENABLE_CS);
+ mdelay(1);
+
+ /* write ucode */
+ /**
+ * @bug
+ * Do NOT set indirect address register once and then
+ * store data to indirect data register in the loop.
+ * It seems very reasonable, but in this case DINO do not
+ * accept ucode. It is essential to set address each time.
+ */
+ /* load new ipw uCode */
+ for (i = 0; i < len / 2; i++)
+ ipw_write_reg16(priv, CX2_BASEBAND_CONTROL_STORE, image[i]);
+
+
+ /* enable DINO */
+ ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS, 0);
+ ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS,
+ DINO_ENABLE_SYSTEM );
+
+ /* this is where the igx / win driver deveates from the VAP driver.*/
+
+ /* wait for alive response */
+ for (i = 0; i < 100; i++) {
+ /* poll for incoming data */
+ cr = ipw_read_reg8(priv, CX2_BASEBAND_CONTROL_STATUS);
+ if (cr & DINO_RXFIFO_DATA)
+ break;
+ mdelay(1);
+ }
+
+ if (cr & DINO_RXFIFO_DATA) {
+ /* alive_command_responce size is NOT multiple of 4 */
+ u32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
+
+ for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
+ response_buffer[i] =
+ ipw_read_reg32(priv,
+ CX2_BASEBAND_RX_FIFO_READ);
+ memcpy(&priv->dino_alive, response_buffer,
+ sizeof(priv->dino_alive));
+ if (priv->dino_alive.alive_command == 1
+ && priv->dino_alive.ucode_valid == 1) {
+ rc = 0;
+ IPW_DEBUG_INFO(
+ "Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
+ "of %02d/%02d/%02d %02d:%02d\n",
+ priv->dino_alive.software_revision,
+ priv->dino_alive.software_revision,
+ priv->dino_alive.device_identifier,
+ priv->dino_alive.device_identifier,
+ priv->dino_alive.time_stamp[0],
+ priv->dino_alive.time_stamp[1],
+ priv->dino_alive.time_stamp[2],
+ priv->dino_alive.time_stamp[3],
+ priv->dino_alive.time_stamp[4]);
+ } else {
+ IPW_DEBUG_INFO("Microcode is not alive\n");
+ rc = -EINVAL;
+ }
+ } else {
+ IPW_DEBUG_INFO("No alive response from DINO\n");
+ rc = -ETIME;
+ }
+
+ /* disable DINO, otherwise for some reason
+ firmware have problem getting alive resp. */
+ ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS, 0);
+
+// spin_unlock_irqrestore(&priv->lock, flags);
+
+ return rc;
+}
+
+static int ipw_load_firmware(struct ipw_priv *priv, u8 * data,
+ size_t len)
+{
+ int rc = -1;
+ int offset = 0;
+ struct fw_chunk *chunk;
+ dma_addr_t shared_phys;
+ u8 *shared_virt;
+
+ IPW_DEBUG_TRACE("<< : \n");
+ shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys);
+
+ if (!shared_virt)
+ return -ENOMEM;
+
+ memmove(shared_virt, data, len);
+
+ /* Start the Dma */
+ rc = ipw_fw_dma_enable(priv);
+
+ if (priv->sram_desc.last_cb_index > 0) {
+ /* the DMA is already ready this would be a bug. */
+ BUG();
+ goto out;
+ }
+
+ do {
+ chunk = (struct fw_chunk *)(data + offset);
+ offset += sizeof(struct fw_chunk);
+ /* build DMA packet and queue up for sending */
+ /* dma to chunk->address, the chunk->length bytes from data +
+ * offeset*/
+ /* Dma loading */
+ rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset,
+ chunk->address, chunk->length);
+ if (rc) {
+ IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
+ goto out;
+ }
+
+ offset += chunk->length;
+ } while (offset < len);
+
+ /* Run the DMA and wait for the answer*/
+ rc = ipw_fw_dma_kick(priv);
+ if (rc) {
+ IPW_ERROR("dmaKick Failed\n");
+ goto out;
+ }
+
+ rc = ipw_fw_dma_wait(priv);
+ if (rc) {
+ IPW_ERROR("dmaWaitSync Failed\n");
+ goto out;
+ }
+ out:
+ pci_free_consistent( priv->pci_dev, len, shared_virt, shared_phys);
+ return rc;
+}
+
+/* stop nic */
+static int ipw_stop_nic(struct ipw_priv *priv)
+{
+ int rc = 0;
+
+ /* stop*/
+ ipw_write32(priv, CX2_RESET_REG, CX2_RESET_REG_STOP_MASTER);
+
+ rc = ipw_poll_bit(priv, CX2_RESET_REG,
+ CX2_RESET_REG_MASTER_DISABLED, 500);
+ if (rc < 0) {
+ IPW_ERROR("wait for reg master disabled failed\n");
+ return rc;
+ }
+
+ ipw_set_bit(priv, CX2_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
+
+ return rc;
+}
+
+static void ipw_start_nic(struct ipw_priv *priv)
+{
+ IPW_DEBUG_TRACE(">>\n");
+
+ /* prvHwStartNic release ARC*/
+ ipw_clear_bit(priv, CX2_RESET_REG,
+ CX2_RESET_REG_MASTER_DISABLED |
+ CX2_RESET_REG_STOP_MASTER |
+ CBD_RESET_REG_PRINCETON_RESET);
+
+ /* enable power management */
+ ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
+
+ IPW_DEBUG_TRACE("<<\n");
+}
+
+static int ipw_init_nic(struct ipw_priv *priv)
+{
+ int rc;
+
+ IPW_DEBUG_TRACE(">>\n");
+ /* reset */
+ /*prvHwInitNic */
+ /* set "initialization complete" bit to move adapter to D0 state */
+ ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_GP_CNTRL_BIT_INIT_DONE);
+
+ /* low-level PLL activation */
+ ipw_write32(priv, CX2_READ_INT_REGISTER, CX2_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
+
+ /* wait for clock stabilization */
+ rc = ipw_poll_bit(priv, CX2_GP_CNTRL_RW,
+ CX2_GP_CNTRL_BIT_CLOCK_READY, 250);
+ if (rc < 0 )
+ IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
+
+ /* assert SW reset */
+ ipw_set_bit(priv, CX2_RESET_REG, CX2_RESET_REG_SW_RESET);
+
+ udelay(10);
+
+ /* set "initialization complete" bit to move adapter to D0 state */
+ ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_GP_CNTRL_BIT_INIT_DONE);
+
+ IPW_DEBUG_TRACE(">>\n");
+ return 0;
+}
+
+
+/* Call this function from process context, it will sleep in request_firmware.
+ * Probe is an ok place to call this from.
+ */
+static int ipw_reset_nic(struct ipw_priv *priv)
+{
+ int rc = 0;
+
+ IPW_DEBUG_TRACE(">>\n");
+
+ rc = ipw_init_nic(priv);
+
+ /* Clear the 'host command active' bit... */
+ priv->status &= ~STATUS_HCMD_ACTIVE;
+ wake_up_interruptible(&priv->wait_command_queue);
+
+ IPW_DEBUG_TRACE("<<\n");
+ return rc;
+}
+
+static int ipw_get_fw(struct ipw_priv *priv,
+ const struct firmware **fw, const char *name)
+{
+ struct fw_header *header;
+ int rc;
+
+ /* ask firmware_class module to get the boot firmware off disk */
+ rc = request_firmware(fw, name, &priv->pci_dev->dev);
+ if (rc < 0) {
+ IPW_ERROR("%s load failed: Reason %d\n", name, rc);
+ return rc;
+ }
+
+ header = (struct fw_header *)(*fw)->data;
+ if (IPW_FW_MAJOR(header->version) != IPW_FW_MAJOR_VERSION) {
+ IPW_ERROR("'%s' firmware version not compatible (%d != %d)\n",
+ name,
+ IPW_FW_MAJOR(header->version), IPW_FW_MAJOR_VERSION);
+ return -EINVAL;
+ }
+
+ IPW_DEBUG_INFO("Loading firmware '%s' file v%d.%d (%zd bytes)\n",
+ name,
+ IPW_FW_MAJOR(header->version),
+ IPW_FW_MINOR(header->version),
+ (*fw)->size - sizeof(struct fw_header));
+ return 0;
+}
+
+#define CX2_RX_BUF_SIZE (3000)
+
+static inline void ipw_rx_queue_reset(struct ipw_priv *priv,
+ struct ipw_rx_queue *rxq)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&rxq->lock, flags);
+
+ INIT_LIST_HEAD(&rxq->rx_free);
+ INIT_LIST_HEAD(&rxq->rx_used);
+
+ /* Fill the rx_used queue with _all_ of the Rx buffers */
+ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
+ /* In the reset function, these buffers may have been allocated
+ * to an SKB, so we need to unmap and free potential storage */
+ if (rxq->pool[i].skb != NULL) {
+ pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
+ CX2_RX_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(rxq->pool[i].skb);
+ }
+ list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+ }
+
+ /* Set us so that we have processed and used all buffers, but have
+ * not restocked the Rx queue with fresh buffers */
+ rxq->read = rxq->write = 0;
+ rxq->processed = RX_QUEUE_SIZE - 1;
+ rxq->free_count = 0;
+ spin_unlock_irqrestore(&rxq->lock, flags);
+}
+
+#ifdef CONFIG_PM
+static int fw_loaded = 0;
+static const struct firmware *bootfw = NULL;
+static const struct firmware *firmware = NULL;
+static const struct firmware *ucode = NULL;
+#endif
+
+static int ipw_load(struct ipw_priv *priv)
+{
+#ifndef CONFIG_PM
+ const struct firmware *bootfw = NULL;
+ const struct firmware *firmware = NULL;
+ const struct firmware *ucode = NULL;
+#endif
+ int rc = 0, retries = 3;
+
+#ifdef CONFIG_PM
+ if (!fw_loaded) {
+#endif
+ rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot"));
+ if (rc)
+ goto error;
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ rc = ipw_get_fw(priv, &ucode,
+ IPW_FW_NAME("ibss_ucode"));
+ if (rc)
+ goto error;
+
+ rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("ibss"));
+ break;
+
+#ifdef CONFIG_IPW_PROMISC
+ case IW_MODE_MONITOR:
+ rc = ipw_get_fw(priv, &ucode,
+ IPW_FW_NAME("ibss_ucode"));
+ if (rc)
+ goto error;
+
+ rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("sniffer"));
+ break;
+#endif
+ case IW_MODE_INFRA:
+ rc = ipw_get_fw(priv, &ucode,
+ IPW_FW_NAME("bss_ucode"));
+ if (rc)
+ goto error;
+
+ rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("bss"));
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ if (rc)
+ goto error;
+
+#ifdef CONFIG_PM
+ fw_loaded = 1;
+ }
+#endif
+
+ if (!priv->rxq)
+ priv->rxq = ipw_rx_queue_alloc(priv);
+ else
+ ipw_rx_queue_reset(priv, priv->rxq);
+ if (!priv->rxq) {
+ IPW_ERROR("Unable to initialize Rx queue\n");
+ goto error;
+ }
+
+ retry:
+ /* Ensure interrupts are disabled */
+ ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
+ priv->status &= ~STATUS_INT_ENABLED;
+
+ /* ack pending interrupts */
+ ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL);
+
+ ipw_stop_nic(priv);
+
+ rc = ipw_reset_nic(priv);
+ if (rc) {
+ IPW_ERROR("Unable to reset NIC\n");
+ goto error;
+ }
+
+ ipw_zero_memory(priv, CX2_NIC_SRAM_LOWER_BOUND,
+ CX2_NIC_SRAM_UPPER_BOUND - CX2_NIC_SRAM_LOWER_BOUND);
+
+ /* DMA the initial boot firmware into the device */
+ rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header),
+ bootfw->size - sizeof(struct fw_header));
+ if (rc < 0) {
+ IPW_ERROR("Unable to load boot firmware\n");
+ goto error;
+ }
+
+ /* kick start the device */
+ ipw_start_nic(priv);
+
+ /* wait for the device to finish it's initial startup sequence */
+ rc = ipw_poll_bit(priv, CX2_INTA_RW,
+ CX2_INTA_BIT_FW_INITIALIZATION_DONE, 500);
+ if (rc < 0) {
+ IPW_ERROR("device failed to boot initial fw image\n");
+ goto error;
+ }
+ IPW_DEBUG_INFO("initial device response after %dms\n", rc);
+
+ /* ack fw init done interrupt */
+ ipw_write32(priv, CX2_INTA_RW, CX2_INTA_BIT_FW_INITIALIZATION_DONE);
+
+ /* DMA the ucode into the device */
+ rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header),
+ ucode->size - sizeof(struct fw_header));
+ if (rc < 0) {
+ IPW_ERROR("Unable to load ucode\n");
+ goto error;
+ }
+
+ /* stop nic */
+ ipw_stop_nic(priv);
+
+ /* DMA bss firmware into the device */
+ rc = ipw_load_firmware(priv, firmware->data +
+ sizeof(struct fw_header),
+ firmware->size - sizeof(struct fw_header));
+ if (rc < 0 ) {
+ IPW_ERROR("Unable to load firmware\n");
+ goto error;
+ }
+
+ ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
+
+ rc = ipw_queue_reset(priv);
+ if (rc) {
+ IPW_ERROR("Unable to initialize queues\n");
+ goto error;
+ }
+
+ /* Ensure interrupts are disabled */
+ ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);
+
+ /* kick start the device */
+ ipw_start_nic(priv);
+
+ if (ipw_read32(priv, CX2_INTA_RW) & CX2_INTA_BIT_PARITY_ERROR) {
+ if (retries > 0) {
+ IPW_WARNING("Parity error. Retrying init.\n");
+ retries--;
+ goto retry;
+ }
+
+ IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
+ rc = -EIO;
+ goto error;
+ }
+
+ /* wait for the device */
+ rc = ipw_poll_bit(priv, CX2_INTA_RW,
+ CX2_INTA_BIT_FW_INITIALIZATION_DONE, 500);
+ if (rc < 0) {
+ IPW_ERROR("device failed to start after 500ms\n");
+ goto error;
+ }
+ IPW_DEBUG_INFO("device response after %dms\n", rc);
+
+ /* ack fw init done interrupt */
+ ipw_write32(priv, CX2_INTA_RW, CX2_INTA_BIT_FW_INITIALIZATION_DONE);
+
+ /* read eeprom data and initialize the eeprom region of sram */
+ priv->eeprom_delay = 1;
+ ipw_eeprom_init_sram(priv);
+
+ /* enable interrupts */
+ ipw_enable_interrupts(priv);
+
+ /* Ensure our queue has valid packets */
+ ipw_rx_queue_replenish(priv);
+
+ ipw_write32(priv, CX2_RX_READ_INDEX, priv->rxq->read);
+
+ /* ack pending interrupts */
+ ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL);
+
+#ifndef CONFIG_PM
+ release_firmware(bootfw);
+ release_firmware(ucode);
+ release_firmware(firmware);
+#endif
+ return 0;
+
+ error:
+ if (priv->rxq) {
+ ipw_rx_queue_free(priv, priv->rxq);
+ priv->rxq = NULL;
+ }
+ ipw_tx_queue_free(priv);
+ if (bootfw)
+ release_firmware(bootfw);
+ if (ucode)
+ release_firmware(ucode);
+ if (firmware)
+ release_firmware(firmware);
+#ifdef CONFIG_PM
+ fw_loaded = 0;
+ bootfw = ucode = firmware = NULL;
+#endif
+
+ return rc;
+}
+
+/**
+ * DMA services
+ *
+ * Theory of operation
+ *
+ * A queue is a circular buffers with 'Read' and 'Write' pointers.
+ * 2 empty entries always kept in the buffer to protect from overflow.
+ *
+ * For Tx queue, there are low mark and high mark limits. If, after queuing
+ * the packet for Tx, free space become < low mark, Tx queue stopped. When
+ * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
+ * Tx queue resumed.
+ *
+ * The IPW operates with six queues, one receive queue in the device's
+ * sram, one transmit queue for sending commands to the device firmware,
+ * and four transmit queues for data.
+ *
+ * The four transmit queues allow for performing quality of service (qos)
+ * transmissions as per the 802.11 protocol. Currently Linux does not
+ * provide a mechanism to the user for utilizing prioritized queues, so
+ * we only utilize the first data transmit queue (queue1).
+ */
+
+/**
+ * Driver allocates buffers of this size for Rx
+ */
+
+static inline int ipw_queue_space(const struct clx2_queue *q)
+{
+ int s = q->last_used - q->first_empty;
+ if (s <= 0)
+ s += q->n_bd;
+ s -= 2; /* keep some reserve to not confuse empty and full situations */
+ if (s < 0)
+ s = 0;
+ return s;
+}
+
+static inline int ipw_queue_inc_wrap(int index, int n_bd)
+{
+ return (++index == n_bd) ? 0 : index;
+}
+
+/**
+ * Initialize common DMA queue structure
+ *
+ * @param q queue to init
+ * @param count Number of BD's to allocate. Should be power of 2
+ * @param read_register Address for 'read' register
+ * (not offset within BAR, full address)
+ * @param write_register Address for 'write' register
+ * (not offset within BAR, full address)
+ * @param base_register Address for 'base' register
+ * (not offset within BAR, full address)
+ * @param size Address for 'size' register
+ * (not offset within BAR, full address)
+ */
+static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
+ int count, u32 read, u32 write,
+ u32 base, u32 size)
+{
+ q->n_bd = count;
+
+ q->low_mark = q->n_bd / 4;
+ if (q->low_mark < 4)
+ q->low_mark = 4;
+
+ q->high_mark = q->n_bd / 8;
+ if (q->high_mark < 2)
+ q->high_mark = 2;
+
+ q->first_empty = q->last_used = 0;
+ q->reg_r = read;
+ q->reg_w = write;
+
+ ipw_write32(priv, base, q->dma_addr);
+ ipw_write32(priv, size, count);
+ ipw_write32(priv, read, 0);
+ ipw_write32(priv, write, 0);
+
+ _ipw_read32(priv, 0x90);
+}
+
+static int ipw_queue_tx_init(struct ipw_priv *priv,
+ struct clx2_tx_queue *q,
+ int count, u32 read, u32 write,
+ u32 base, u32 size)
+{
+ struct pci_dev *dev = priv->pci_dev;
+
+ q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL);
+ if (!q->txb) {
+ IPW_ERROR("vmalloc for auxilary BD structures failed\n");
+ return -ENOMEM;
+ }
+
+ q->bd = pci_alloc_consistent(dev,sizeof(q->bd[0])*count, &q->q.dma_addr);
+ if (!q->bd) {
+ IPW_ERROR("pci_alloc_consistent(%zd) failed\n",
+ sizeof(q->bd[0]) * count);
+ kfree(q->txb);
+ q->txb = NULL;
+ return -ENOMEM;
+ }
+
+ ipw_queue_init(priv, &q->q, count, read, write, base, size);
+ return 0;
+}
+
+/**
+ * Free one TFD, those at index [txq->q.last_used].
+ * Do NOT advance any indexes
+ *
+ * @param dev
+ * @param txq
+ */
+static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
+ struct clx2_tx_queue *txq)
+{
+ struct tfd_frame *bd = &txq->bd[txq->q.last_used];
+ struct pci_dev *dev = priv->pci_dev;
+ int i;
+
+ /* classify bd */
+ if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
+ /* nothing to cleanup after for host commands */
+ return;
+
+ /* sanity check */
+ if (bd->u.data.num_chunks > NUM_TFD_CHUNKS) {
+ IPW_ERROR("Too many chunks: %i\n", bd->u.data.num_chunks);
+ /** @todo issue fatal error, it is quite serious situation */
+ return;
+ }
+
+ /* unmap chunks if any */
+ for (i = 0; i < bd->u.data.num_chunks; i++) {
+ pci_unmap_single(dev, bd->u.data.chunk_ptr[i],
+ bd->u.data.chunk_len[i], PCI_DMA_TODEVICE);
+ if (txq->txb[txq->q.last_used]) {
+ ieee80211_txb_free(txq->txb[txq->q.last_used]);
+ txq->txb[txq->q.last_used] = NULL;
+ }
+ }
+}
+
+/**
+ * Deallocate DMA queue.
+ *
+ * Empty queue by removing and destroying all BD's.
+ * Free all buffers.
+ *
+ * @param dev
+ * @param q
+ */
+static void ipw_queue_tx_free(struct ipw_priv *priv,
+ struct clx2_tx_queue *txq)
+{
+ struct clx2_queue *q = &txq->q;
+ struct pci_dev *dev = priv->pci_dev;
+
+ if (q->n_bd == 0)
+ return;
+
+ /* first, empty all BD's */
+ for (; q->first_empty != q->last_used;
+ q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
+ ipw_queue_tx_free_tfd(priv, txq);
+ }
+
+ /* free buffers belonging to queue itself */
+ pci_free_consistent(dev, sizeof(txq->bd[0])*q->n_bd, txq->bd,
+ q->dma_addr);
+ kfree(txq->txb);
+
+ /* 0 fill whole structure */
+ memset(txq, 0, sizeof(*txq));
+}
+
+
+/**
+ * Destroy all DMA queues and structures
+ *
+ * @param priv
+ */
+static void ipw_tx_queue_free(struct ipw_priv *priv)
+{
+ /* Tx CMD queue */
+ ipw_queue_tx_free(priv, &priv->txq_cmd);
+
+ /* Tx queues */
+ ipw_queue_tx_free(priv, &priv->txq[0]);
+ ipw_queue_tx_free(priv, &priv->txq[1]);
+ ipw_queue_tx_free(priv, &priv->txq[2]);
+ ipw_queue_tx_free(priv, &priv->txq[3]);
+}
+
+static void inline __maybe_wake_tx(struct ipw_priv *priv)
+{
+ if (netif_running(priv->net_dev)) {
+ switch (priv->port_type) {
+ case DCR_TYPE_MU_BSS:
+ case DCR_TYPE_MU_IBSS:
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ return;
+ }
+ }
+ netif_wake_queue(priv->net_dev);
+ }
+
+}
+
+static inline void ipw_create_bssid(struct ipw_priv *priv, u8 *bssid)
+{
+ /* First 3 bytes are manufacturer */
+ bssid[0] = priv->mac_addr[0];
+ bssid[1] = priv->mac_addr[1];
+ bssid[2] = priv->mac_addr[2];
+
+ /* Last bytes are random */
+ get_random_bytes(&bssid[3], ETH_ALEN-3);
+
+ bssid[0] &= 0xfe; /* clear multicast bit */
+ bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */
+}
+
+static inline u8 ipw_add_station(struct ipw_priv *priv, u8 *bssid)
+{
+ struct ipw_station_entry entry;
+ int i;
+
+ for (i = 0; i < priv->num_stations; i++) {
+ if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) {
+ /* Another node is active in network */
+ priv->missed_adhoc_beacons = 0;
+ if (!(priv->config & CFG_STATIC_CHANNEL))
+ /* when other nodes drop out, we drop out */
+ priv->config &= ~CFG_ADHOC_PERSIST;
+
+ return i;
+ }
+ }
+
+ if (i == MAX_STATIONS)
+ return IPW_INVALID_STATION;
+
+ IPW_DEBUG_SCAN("Adding AdHoc station: " MAC_FMT "\n", MAC_ARG(bssid));
+
+ entry.reserved = 0;
+ entry.support_mode = 0;
+ memcpy(entry.mac_addr, bssid, ETH_ALEN);
+ memcpy(priv->stations[i], bssid, ETH_ALEN);
+ ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
+ &entry,
+ sizeof(entry));
+ priv->num_stations++;
+
+ return i;
+}
+
+static inline u8 ipw_find_station(struct ipw_priv *priv, u8 *bssid)
+{
+ int i;
+
+ for (i = 0; i < priv->num_stations; i++)
+ if (!memcmp(priv->stations[i], bssid, ETH_ALEN))
+ return i;
+
+ return IPW_INVALID_STATION;
+}
+
+static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
+{
+ int err;
+
+ if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))) {
+ IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
+ return;
+ }
+
+ IPW_DEBUG_ASSOC("Disassocation attempt from " MAC_FMT " "
+ "on channel %d.\n",
+ MAC_ARG(priv->assoc_request.bssid),
+ priv->assoc_request.channel);
+
+ priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
+ priv->status |= STATUS_DISASSOCIATING;
+
+ if (quiet)
+ priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
+ else
+ priv->assoc_request.assoc_type = HC_DISASSOCIATE;
+ err = ipw_send_associate(priv, &priv->assoc_request);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send [dis]associate command "
+ "failed.\n");
+ return;
+ }
+
+}
+
+static void ipw_disassociate(void *data)
+{
+ ipw_send_disassociate(data, 0);
+}
+
+static void notify_wx_assoc_event(struct ipw_priv *priv)
+{
+ union iwreq_data wrqu;
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ if (priv->status & STATUS_ASSOCIATED)
+ memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
+ else
+ memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+ wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+struct ipw_status_code {
+ u16 status;
+ const char *reason;
+};
+
+static const struct ipw_status_code ipw_status_codes[] = {
+ {0x00, "Successful"},
+ {0x01, "Unspecified failure"},
+ {0x0A, "Cannot support all requested capabilities in the "
+ "Capability information field"},
+ {0x0B, "Reassociation denied due to inability to confirm that "
+ "association exists"},
+ {0x0C, "Association denied due to reason outside the scope of this "
+ "standard"},
+ {0x0D, "Responding station does not support the specified authentication "
+ "algorithm"},
+ {0x0E, "Received an Authentication frame with authentication sequence "
+ "transaction sequence number out of expected sequence"},
+ {0x0F, "Authentication rejected because of challenge failure"},
+ {0x10, "Authentication rejected due to timeout waiting for next "
+ "frame in sequence"},
+ {0x11, "Association denied because AP is unable to handle additional "
+ "associated stations"},
+ {0x12, "Association denied due to requesting station not supporting all "
+ "of the datarates in the BSSBasicServiceSet Parameter"},
+ {0x13, "Association denied due to requesting station not supporting "
+ "short preamble operation"},
+ {0x14, "Association denied due to requesting station not supporting "
+ "PBCC encoding"},
+ {0x15, "Association denied due to requesting station not supporting "
+ "channel agility"},
+ {0x19, "Association denied due to requesting station not supporting "
+ "short slot operation"},
+ {0x1A, "Association denied due to requesting station not supporting "
+ "DSSS-OFDM operation"},
+ {0x28, "Invalid Information Element"},
+ {0x29, "Group Cipher is not valid"},
+ {0x2A, "Pairwise Cipher is not valid"},
+ {0x2B, "AKMP is not valid"},
+ {0x2C, "Unsupported RSN IE version"},
+ {0x2D, "Invalid RSN IE Capabilities"},
+ {0x2E, "Cipher suite is rejected per security policy"},
+};
+
+#ifdef CONFIG_IPW_DEBUG
+static const char *ipw_get_status_code(u16 status)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
+ if (ipw_status_codes[i].status == status)
+ return ipw_status_codes[i].reason;
+ return "Unknown status value.";
+}
+#endif
+
+static void inline average_init(struct average *avg)
+{
+ memset(avg, 0, sizeof(*avg));
+}
+
+static void inline average_add(struct average *avg, s16 val)
+{
+ avg->sum -= avg->entries[avg->pos];
+ avg->sum += val;
+ avg->entries[avg->pos++] = val;
+ if (unlikely(avg->pos == AVG_ENTRIES)) {
+ avg->init = 1;
+ avg->pos = 0;
+ }
+}
+
+static s16 inline average_value(struct average *avg)
+{
+ if (!unlikely(avg->init)) {
+ if (avg->pos)
+ return avg->sum / avg->pos;
+ return 0;
+ }
+
+ return avg->sum / AVG_ENTRIES;
+}
+
+static void ipw_reset_stats(struct ipw_priv *priv)
+{
+ u32 len = sizeof(u32);
+
+ priv->quality = 0;
+
+ average_init(&priv->average_missed_beacons);
+ average_init(&priv->average_rssi);
+ average_init(&priv->average_noise);
+
+ priv->last_rate = 0;
+ priv->last_missed_beacons = 0;
+ priv->last_rx_packets = 0;
+ priv->last_tx_packets = 0;
+ priv->last_tx_failures = 0;
+
+ /* Firmware managed, reset only when NIC is restarted, so we have to
+ * normalize on the current value */
+ ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
+ &priv->last_rx_err, &len);
+ ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
+ &priv->last_tx_failures, &len);
+
+ /* Driver managed, reset with each association */
+ priv->missed_adhoc_beacons = 0;
+ priv->missed_beacons = 0;
+ priv->tx_packets = 0;
+ priv->rx_packets = 0;
+
+}
+
+
+static inline u32 ipw_get_max_rate(struct ipw_priv *priv)
+{
+ u32 i = 0x80000000;
+ u32 mask = priv->rates_mask;
+ /* If currently associated in B mode, restrict the maximum
+ * rate match to B rates */
+ if (priv->assoc_request.ieee_mode == IPW_B_MODE)
+ mask &= IEEE80211_CCK_RATES_MASK;
+
+ /* TODO: Verify that the rate is supported by the current rates
+ * list. */
+
+ while (i && !(mask & i)) i >>= 1;
+ switch (i) {
+ case IEEE80211_CCK_RATE_1MB_MASK: return 1000000;
+ case IEEE80211_CCK_RATE_2MB_MASK: return 2000000;
+ case IEEE80211_CCK_RATE_5MB_MASK: return 5500000;
+ case IEEE80211_OFDM_RATE_6MB_MASK: return 6000000;
+ case IEEE80211_OFDM_RATE_9MB_MASK: return 9000000;
+ case IEEE80211_CCK_RATE_11MB_MASK: return 11000000;
+ case IEEE80211_OFDM_RATE_12MB_MASK: return 12000000;
+ case IEEE80211_OFDM_RATE_18MB_MASK: return 18000000;
+ case IEEE80211_OFDM_RATE_24MB_MASK: return 24000000;
+ case IEEE80211_OFDM_RATE_36MB_MASK: return 36000000;
+ case IEEE80211_OFDM_RATE_48MB_MASK: return 48000000;
+ case IEEE80211_OFDM_RATE_54MB_MASK: return 54000000;
+ }
+
+ if (priv->ieee->mode == IEEE_B)
+ return 11000000;
+ else
+ return 54000000;
+}
+
+static u32 ipw_get_current_rate(struct ipw_priv *priv)
+{
+ u32 rate, len = sizeof(rate);
+ int err;
+
+ if (!(priv->status & STATUS_ASSOCIATED))
+ return 0;
+
+ if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
+ err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
+ &len);
+ if (err) {
+ IPW_DEBUG_INFO("failed querying ordinals.\n");
+ return 0;
+ }
+ } else
+ return ipw_get_max_rate(priv);
+
+ switch (rate) {
+ case IPW_TX_RATE_1MB: return 1000000;
+ case IPW_TX_RATE_2MB: return 2000000;
+ case IPW_TX_RATE_5MB: return 5500000;
+ case IPW_TX_RATE_6MB: return 6000000;
+ case IPW_TX_RATE_9MB: return 9000000;
+ case IPW_TX_RATE_11MB: return 11000000;
+ case IPW_TX_RATE_12MB: return 12000000;
+ case IPW_TX_RATE_18MB: return 18000000;
+ case IPW_TX_RATE_24MB: return 24000000;
+ case IPW_TX_RATE_36MB: return 36000000;
+ case IPW_TX_RATE_48MB: return 48000000;
+ case IPW_TX_RATE_54MB: return 54000000;
+ }
+
+ return 0;
+}
+
+#define PERFECT_RSSI (-50)
+#define WORST_RSSI (-85)
+#define IPW_STATS_INTERVAL (2 * HZ)
+static void ipw_gather_stats(struct ipw_priv *priv)
+{
+ u32 rx_err, rx_err_delta, rx_packets_delta;
+ u32 tx_failures, tx_failures_delta, tx_packets_delta;
+ u32 missed_beacons_percent, missed_beacons_delta;
+ u32 quality = 0;
+ u32 len = sizeof(u32);
+ s16 rssi;
+ u32 beacon_quality, signal_quality, tx_quality, rx_quality,
+ rate_quality;
+
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ priv->quality = 0;
+ return;
+ }
+
+ /* Update the statistics */
+ ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
+ &priv->missed_beacons, &len);
+ missed_beacons_delta = priv->missed_beacons -
+ priv->last_missed_beacons;
+ priv->last_missed_beacons = priv->missed_beacons;
+ if (priv->assoc_request.beacon_interval) {
+ missed_beacons_percent = missed_beacons_delta *
+ (HZ * priv->assoc_request.beacon_interval) /
+ (IPW_STATS_INTERVAL * 10);
+ } else {
+ missed_beacons_percent = 0;
+ }
+ average_add(&priv->average_missed_beacons, missed_beacons_percent);
+
+ ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
+ rx_err_delta = rx_err - priv->last_rx_err;
+ priv->last_rx_err = rx_err;
+
+ ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
+ tx_failures_delta = tx_failures - priv->last_tx_failures;
+ priv->last_tx_failures = tx_failures;
+
+ rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
+ priv->last_rx_packets = priv->rx_packets;
+
+ tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
+ priv->last_tx_packets = priv->tx_packets;
+
+ /* Calculate quality based on the following:
+ *
+ * Missed beacon: 100% = 0, 0% = 70% missed
+ * Rate: 60% = 1Mbs, 100% = Max
+ * Rx and Tx errors represent a straight % of total Rx/Tx
+ * RSSI: 100% = > -50, 0% = < -80
+ * Rx errors: 100% = 0, 0% = 50% missed
+ *
+ * The lowest computed quality is used.
+ *
+ */
+#define BEACON_THRESHOLD 5
+ beacon_quality = 100 - missed_beacons_percent;
+ if (beacon_quality < BEACON_THRESHOLD)
+ beacon_quality = 0;
+ else
+ beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
+ (100 - BEACON_THRESHOLD);
+ IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
+ beacon_quality, missed_beacons_percent);
+
+ priv->last_rate = ipw_get_current_rate(priv);
+ rate_quality = priv->last_rate * 40 / priv->last_rate + 60;
+ IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
+ rate_quality, priv->last_rate / 1000000);
+
+ if (rx_packets_delta > 100 &&
+ rx_packets_delta + rx_err_delta)
+ rx_quality = 100 - (rx_err_delta * 100) /
+ (rx_packets_delta + rx_err_delta);
+ else
+ rx_quality = 100;
+ IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n",
+ rx_quality, rx_err_delta, rx_packets_delta);
+
+ if (tx_packets_delta > 100 &&
+ tx_packets_delta + tx_failures_delta)
+ tx_quality = 100 - (tx_failures_delta * 100) /
+ (tx_packets_delta + tx_failures_delta);
+ else
+ tx_quality = 100;
+ IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n",
+ tx_quality, tx_failures_delta, tx_packets_delta);
+
+ rssi = average_value(&priv->average_rssi);
+ if (rssi > PERFECT_RSSI)
+ signal_quality = 100;
+ else if (rssi < WORST_RSSI)
+ signal_quality = 0;
+ else
+ signal_quality = (rssi - WORST_RSSI) * 100 /
+ (PERFECT_RSSI - WORST_RSSI);
+ IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
+ signal_quality, rssi);
+
+ quality = min(beacon_quality,
+ min(rate_quality,
+ min(tx_quality, min(rx_quality, signal_quality))));
+ if (quality == beacon_quality)
+ IPW_DEBUG_STATS(
+ "Quality (%d%%): Clamped to missed beacons.\n",
+ quality);
+ if (quality == rate_quality)
+ IPW_DEBUG_STATS(
+ "Quality (%d%%): Clamped to rate quality.\n",
+ quality);
+ if (quality == tx_quality)
+ IPW_DEBUG_STATS(
+ "Quality (%d%%): Clamped to Tx quality.\n",
+ quality);
+ if (quality == rx_quality)
+ IPW_DEBUG_STATS(
+ "Quality (%d%%): Clamped to Rx quality.\n",
+ quality);
+ if (quality == signal_quality)
+ IPW_DEBUG_STATS(
+ "Quality (%d%%): Clamped to signal quality.\n",
+ quality);
+
+ priv->quality = quality;
+
+ queue_delayed_work(priv->workqueue, &priv->gather_stats,
+ IPW_STATS_INTERVAL);
+}
+
+/**
+ * Handle host notification packet.
+ * Called from interrupt routine
+ */
+static inline void ipw_rx_notification(struct ipw_priv* priv,
+ struct ipw_rx_notification *notif)
+{
+ IPW_DEBUG_NOTIF("type = %i (%d bytes)\n",
+ notif->subtype, notif->size);
+
+ switch (notif->subtype) {
+ case HOST_NOTIFICATION_STATUS_ASSOCIATED: {
+ struct notif_association *assoc = &notif->u.assoc;
+
+ switch (assoc->state) {
+ case CMAS_ASSOCIATED: {
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "associated: '%s' " MAC_FMT " \n",
+ escape_essid(priv->essid, priv->essid_len),
+ MAC_ARG(priv->bssid));
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_INFRA:
+ memcpy(priv->ieee->bssid, priv->bssid,
+ ETH_ALEN);
+ break;
+
+ case IW_MODE_ADHOC:
+ memcpy(priv->ieee->bssid, priv->bssid,
+ ETH_ALEN);
+
+ /* clear out the station table */
+ priv->num_stations = 0;
+
+ IPW_DEBUG_ASSOC("queueing adhoc check\n");
+ queue_delayed_work(priv->workqueue,
+ &priv->adhoc_check,
+ priv->assoc_request.beacon_interval);
+ break;
+ }
+
+ priv->status &= ~STATUS_ASSOCIATING;
+ priv->status |= STATUS_ASSOCIATED;
+
+ netif_carrier_on(priv->net_dev);
+ if (netif_queue_stopped(priv->net_dev)) {
+ IPW_DEBUG_NOTIF("waking queue\n");
+ netif_wake_queue(priv->net_dev);
+ } else {
+ IPW_DEBUG_NOTIF("starting queue\n");
+ netif_start_queue(priv->net_dev);
+ }
+
+ ipw_reset_stats(priv);
+ /* Ensure the rate is updated immediately */
+ priv->last_rate = ipw_get_current_rate(priv);
+ schedule_work(&priv->gather_stats);
+ notify_wx_assoc_event(priv);
+
+/* queue_delayed_work(priv->workqueue,
+ &priv->request_scan,
+ SCAN_ASSOCIATED_INTERVAL);
+*/
+ break;
+ }
+
+ case CMAS_AUTHENTICATED: {
+ if (priv->status & (STATUS_ASSOCIATED | STATUS_AUTH)) {
+#ifdef CONFIG_IPW_DEBUG
+ struct notif_authenticate *auth = &notif->u.auth;
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "deauthenticated: '%s' " MAC_FMT ": (0x%04X) - %s \n",
+ escape_essid(priv->essid, priv->essid_len),
+ MAC_ARG(priv->bssid),
+ ntohs(auth->status),
+ ipw_get_status_code(ntohs(auth->status)));
+#endif
+
+ priv->status &= ~(STATUS_ASSOCIATING |
+ STATUS_AUTH |
+ STATUS_ASSOCIATED);
+
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+ queue_work(priv->workqueue, &priv->request_scan);
+ notify_wx_assoc_event(priv);
+ break;
+ }
+
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "authenticated: '%s' " MAC_FMT "\n",
+ escape_essid(priv->essid, priv->essid_len),
+ MAC_ARG(priv->bssid));
+ break;
+ }
+
+ case CMAS_INIT: {
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "disassociated: '%s' " MAC_FMT " \n",
+ escape_essid(priv->essid, priv->essid_len),
+ MAC_ARG(priv->bssid));
+
+ priv->status &= ~(
+ STATUS_DISASSOCIATING |
+ STATUS_ASSOCIATING |
+ STATUS_ASSOCIATED |
+ STATUS_AUTH);
+
+ netif_stop_queue(priv->net_dev);
+ if (!(priv->status & STATUS_ROAMING)) {
+ netif_carrier_off(priv->net_dev);
+ notify_wx_assoc_event(priv);
+
+ /* Cancel any queued work ... */
+ cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->adhoc_check);
+
+ /* Queue up another scan... */
+ queue_work(priv->workqueue,
+ &priv->request_scan);
+
+ cancel_delayed_work(&priv->gather_stats);
+ } else {
+ priv->status |= STATUS_ROAMING;
+ queue_work(priv->workqueue,
+ &priv->request_scan);
+ }
+
+ ipw_reset_stats(priv);
+ break;
+ }
+
+ default:
+ IPW_ERROR("assoc: unknown (%d)\n",
+ assoc->state);
+ break;
+ }
+
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_AUTHENTICATE: {
+ struct notif_authenticate *auth = &notif->u.auth;
+ switch (auth->state) {
+ case CMAS_AUTHENTICATED:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
+ "authenticated: '%s' " MAC_FMT " \n",
+ escape_essid(priv->essid, priv->essid_len),
+ MAC_ARG(priv->bssid));
+ priv->status |= STATUS_AUTH;
+ break;
+
+ case CMAS_INIT:
+ if (priv->status & STATUS_AUTH) {
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "authentication failed (0x%04X): %s\n",
+ ntohs(auth->status),
+ ipw_get_status_code(ntohs(auth->status)));
+ }
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "deauthenticated: '%s' " MAC_FMT "\n",
+ escape_essid(priv->essid, priv->essid_len),
+ MAC_ARG(priv->bssid));
+
+ priv->status &= ~(STATUS_ASSOCIATING |
+ STATUS_AUTH |
+ STATUS_ASSOCIATED);
+
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+ queue_work(priv->workqueue, &priv->request_scan);
+ notify_wx_assoc_event(priv);
+ break;
+
+ case CMAS_TX_AUTH_SEQ_1:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "AUTH_SEQ_1\n");
+ break;
+ case CMAS_RX_AUTH_SEQ_2:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "AUTH_SEQ_2\n");
+ break;
+ case CMAS_AUTH_SEQ_1_PASS:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "AUTH_SEQ_1_PASS\n");
+ break;
+ case CMAS_AUTH_SEQ_1_FAIL:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "AUTH_SEQ_1_FAIL\n");
+ break;
+ case CMAS_TX_AUTH_SEQ_3:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "AUTH_SEQ_3\n");
+ break;
+ case CMAS_RX_AUTH_SEQ_4:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "RX_AUTH_SEQ_4\n");
+ break;
+ case CMAS_AUTH_SEQ_2_PASS:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "AUTH_SEQ_2_PASS\n");
+ break;
+ case CMAS_AUTH_SEQ_2_FAIL:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "AUT_SEQ_2_FAIL\n");
+ break;
+ case CMAS_TX_ASSOC:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "TX_ASSOC\n");
+ break;
+ case CMAS_RX_ASSOC_RESP:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "RX_ASSOC_RESP\n");
+ break;
+ case CMAS_ASSOCIATED:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "ASSOCIATED\n");
+ break;
+ default:
+ IPW_DEBUG_NOTIF("auth: failure - %d\n", auth->state);
+ break;
+ }
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT: {
+ struct notif_channel_result *x = &notif->u.channel_result;
+
+ if (notif->size == sizeof(*x)) {
+ IPW_DEBUG_SCAN("Scan result for channel %d\n",
+ x->channel_num);
+ } else {
+ IPW_DEBUG_SCAN("Scan result of wrong size %d "
+ "(should be %zd)\n",
+ notif->size, sizeof(*x));
+ }
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED: {
+ struct notif_scan_complete* x = &notif->u.scan_complete;
+ if (notif->size == sizeof(*x)) {
+ IPW_DEBUG_SCAN("Scan completed: type %d, %d channels, "
+ "%d status\n",
+ x->scan_type,
+ x->num_channels,
+ x->status);
+ } else {
+ IPW_ERROR("Scan completed of wrong size %d "
+ "(should be %zd)\n",
+ notif->size, sizeof(*x));
+ }
+
+ priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
+
+ cancel_delayed_work(&priv->scan_check);
+
+ if (!(priv->status & (STATUS_ASSOCIATED |
+ STATUS_ASSOCIATING |
+ STATUS_ROAMING |
+ STATUS_DISASSOCIATING)))
+ queue_work(priv->workqueue, &priv->associate);
+ else if (priv->status & STATUS_ROAMING) {
+ /* If a scan completed and we are in roam mode, then
+ * the scan that completed was the one requested as a
+ * result of entering roam... so, schedule the
+ * roam work */
+ queue_work(priv->workqueue, &priv->roam);
+ } else if (priv->status & STATUS_SCAN_PENDING)
+ queue_work(priv->workqueue, &priv->request_scan);
+
+ priv->ieee->scans++;
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_FRAG_LENGTH: {
+ struct notif_frag_length *x = &notif->u.frag_len;
+
+ if (notif->size == sizeof(*x)) {
+ IPW_ERROR("Frag length: %d\n", x->frag_length);
+ } else {
+ IPW_ERROR("Frag length of wrong size %d "
+ "(should be %zd)\n",
+ notif->size, sizeof(*x));
+ }
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION: {
+ struct notif_link_deterioration *x =
+ &notif->u.link_deterioration;
+ if (notif->size==sizeof(*x)) {
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
+ "link deterioration: '%s' " MAC_FMT " \n",
+ escape_essid(priv->essid, priv->essid_len),
+ MAC_ARG(priv->bssid));
+ memcpy(&priv->last_link_deterioration, x, sizeof(*x));
+ } else {
+ IPW_ERROR("Link Deterioration of wrong size %d "
+ "(should be %zd)\n",
+ notif->size, sizeof(*x));
+ }
+ break;
+ }
+
+ case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE: {
+ IPW_ERROR("Dino config\n");
+ if (priv->hcmd && priv->hcmd->cmd == HOST_CMD_DINO_CONFIG) {
+ /* TODO: Do anything special? */
+ } else {
+ IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
+ }
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_BEACON_STATE: {
+ struct notif_beacon_state *x = &notif->u.beacon_state;
+ if (notif->size != sizeof(*x)) {
+ IPW_ERROR("Beacon state of wrong size %d (should "
+ "be %zd)\n", notif->size, sizeof(*x));
+ break;
+ }
+
+ if (x->state == HOST_NOTIFICATION_STATUS_BEACON_MISSING) {
+ if (priv->status & STATUS_SCANNING) {
+ /* Stop scan to keep fw from getting
+ * stuck... */
+ queue_work(priv->workqueue,
+ &priv->abort_scan);
+ }
+
+ if (x->number > priv->missed_beacon_threshold &&
+ priv->status & STATUS_ASSOCIATED) {
+ IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
+ IPW_DL_STATE,
+ "Missed beacon: %d - disassociate\n",
+ x->number);
+ queue_work(priv->workqueue,
+ &priv->disassociate);
+ } else if (x->number > priv->roaming_threshold) {
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
+ "Missed beacon: %d - initiate "
+ "roaming\n",
+ x->number);
+ queue_work(priv->workqueue,
+ &priv->roam);
+ } else {
+ IPW_DEBUG_NOTIF("Missed beacon: %d\n",
+ x->number);
+ }
+
+ priv->notif_missed_beacons = x->number;
+
+ }
+
+
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_TGI_TX_KEY: {
+ struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
+ if (notif->size==sizeof(*x)) {
+ IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
+ "0x%02x station %d\n",
+ x->key_state,x->security_type,
+ x->station_index);
+ break;
+ }
+
+ IPW_ERROR("TGi Tx Key of wrong size %d (should be %zd)\n",
+ notif->size, sizeof(*x));
+ break;
+ }
+
+ case HOST_NOTIFICATION_CALIB_KEEP_RESULTS: {
+ struct notif_calibration *x = &notif->u.calibration;
+
+ if (notif->size == sizeof(*x)) {
+ memcpy(&priv->calib, x, sizeof(*x));
+ IPW_DEBUG_INFO("TODO: Calibration\n");
+ break;
+ }
+
+ IPW_ERROR("Calibration of wrong size %d (should be %zd)\n",
+ notif->size, sizeof(*x));
+ break;
+ }
+
+ case HOST_NOTIFICATION_NOISE_STATS: {
+ if (notif->size == sizeof(u32)) {
+ priv->last_noise = (u8)(notif->u.noise.value & 0xff);
+ average_add(&priv->average_noise, priv->last_noise);
+ break;
+ }
+
+ IPW_ERROR("Noise stat is wrong size %d (should be %zd)\n",
+ notif->size, sizeof(u32));
+ break;
+ }
+
+ default:
+ IPW_ERROR("Unknown notification: "
+ "subtype=%d,flags=0x%2x,size=%d\n",
+ notif->subtype, notif->flags, notif->size);
+ }
+}
+
+/**
+ * Destroys all DMA structures and initialise them again
+ *
+ * @param priv
+ * @return error code
+ */
+static int ipw_queue_reset(struct ipw_priv *priv)
+{
+ int rc = 0;
+ /** @todo customize queue sizes */
+ int nTx = 64, nTxCmd = 8;
+ ipw_tx_queue_free(priv);
+ /* Tx CMD queue */
+ rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
+ CX2_TX_CMD_QUEUE_READ_INDEX,
+ CX2_TX_CMD_QUEUE_WRITE_INDEX,
+ CX2_TX_CMD_QUEUE_BD_BASE,
+ CX2_TX_CMD_QUEUE_BD_SIZE);
+ if (rc) {
+ IPW_ERROR("Tx Cmd queue init failed\n");
+ goto error;
+ }
+ /* Tx queue(s) */
+ rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
+ CX2_TX_QUEUE_0_READ_INDEX,
+ CX2_TX_QUEUE_0_WRITE_INDEX,
+ CX2_TX_QUEUE_0_BD_BASE,
+ CX2_TX_QUEUE_0_BD_SIZE);
+ if (rc) {
+ IPW_ERROR("Tx 0 queue init failed\n");
+ goto error;
+ }
+ rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
+ CX2_TX_QUEUE_1_READ_INDEX,
+ CX2_TX_QUEUE_1_WRITE_INDEX,
+ CX2_TX_QUEUE_1_BD_BASE,
+ CX2_TX_QUEUE_1_BD_SIZE);
+ if (rc) {
+ IPW_ERROR("Tx 1 queue init failed\n");
+ goto error;
+ }
+ rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
+ CX2_TX_QUEUE_2_READ_INDEX,
+ CX2_TX_QUEUE_2_WRITE_INDEX,
+ CX2_TX_QUEUE_2_BD_BASE,
+ CX2_TX_QUEUE_2_BD_SIZE);
+ if (rc) {
+ IPW_ERROR("Tx 2 queue init failed\n");
+ goto error;
+ }
+ rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
+ CX2_TX_QUEUE_3_READ_INDEX,
+ CX2_TX_QUEUE_3_WRITE_INDEX,
+ CX2_TX_QUEUE_3_BD_BASE,
+ CX2_TX_QUEUE_3_BD_SIZE);
+ if (rc) {
+ IPW_ERROR("Tx 3 queue init failed\n");
+ goto error;
+ }
+ /* statistics */
+ priv->rx_bufs_min = 0;
+ priv->rx_pend_max = 0;
+ return rc;
+
+ error:
+ ipw_tx_queue_free(priv);
+ return rc;
+}
+
+/**
+ * Reclaim Tx queue entries no more used by NIC.
+ *
+ * When FW adwances 'R' index, all entries between old and
+ * new 'R' index need to be reclaimed. As result, some free space
+ * forms. If there is enough free space (> low mark), wake Tx queue.
+ *
+ * @note Need to protect against garbage in 'R' index
+ * @param priv
+ * @param txq
+ * @param qindex
+ * @return Number of used entries remains in the queue
+ */
+static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
+ struct clx2_tx_queue *txq, int qindex)
+{
+ u32 hw_tail;
+ int used;
+ struct clx2_queue *q = &txq->q;
+
+ hw_tail = ipw_read32(priv, q->reg_r);
+ if (hw_tail >= q->n_bd) {
+ IPW_ERROR
+ ("Read index for DMA queue (%d) is out of range [0-%d)\n",
+ hw_tail, q->n_bd);
+ goto done;
+ }
+ for (; q->last_used != hw_tail;
+ q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
+ ipw_queue_tx_free_tfd(priv, txq);
+ priv->tx_packets++;
+ }
+ done:
+ if (ipw_queue_space(q) > q->low_mark && qindex >= 0) {
+ __maybe_wake_tx(priv);
+ }
+ used = q->first_empty - q->last_used;
+ if (used < 0)
+ used += q->n_bd;
+
+ return used;
+}
+
+static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
+ int len, int sync)
+{
+ struct clx2_tx_queue *txq = &priv->txq_cmd;
+ struct clx2_queue *q = &txq->q;
+ struct tfd_frame *tfd;
+
+ if (ipw_queue_space(q) < (sync ? 1 : 2)) {
+ IPW_ERROR("No space for Tx\n");
+ return -EBUSY;
+ }
+
+ tfd = &txq->bd[q->first_empty];
+ txq->txb[q->first_empty] = NULL;
+
+ memset(tfd, 0, sizeof(*tfd));
+ tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
+ tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
+ priv->hcmd_seq++;
+ tfd->u.cmd.index = hcmd;
+ tfd->u.cmd.length = len;
+ memcpy(tfd->u.cmd.payload, buf, len);
+ q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
+ ipw_write32(priv, q->reg_w, q->first_empty);
+ _ipw_read32(priv, 0x90);
+
+ return 0;
+}
+
+
+
+/*
+ * Rx theory of operation
+ *
+ * The host allocates 32 DMA target addresses and passes the host address
+ * to the firmware at register CX2_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
+ * 0 to 31
+ *
+ * Rx Queue Indexes
+ * The host/firmware share two index registers for managing the Rx buffers.
+ *
+ * The READ index maps to the first position that the firmware may be writing
+ * to -- the driver can read up to (but not including) this position and get
+ * good data.
+ * The READ index is managed by the firmware once the card is enabled.
+ *
+ * The WRITE index maps to the last position the driver has read from -- the
+ * position preceding WRITE is the last slot the firmware can place a packet.
+ *
+ * The queue is empty (no good data) if WRITE = READ - 1, and is full if
+ * WRITE = READ.
+ *
+ * During initialization the host sets up the READ queue position to the first
+ * INDEX position, and WRITE to the last (READ - 1 wrapped)
+ *
+ * When the firmware places a packet in a buffer it will advance the READ index
+ * and fire the RX interrupt. The driver can then query the READ index and
+ * process as many packets as possible, moving the WRITE index forward as it
+ * resets the Rx queue buffers with new memory.
+ *
+ * The management in the driver is as follows:
+ * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When
+ * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
+ * to replensish the ipw->rxq->rx_free.
+ * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the
+ * ipw->rxq is replenished and the READ INDEX is updated (updating the
+ * 'processed' and 'read' driver indexes as well)
+ * + A received packet is processed and handed to the kernel network stack,
+ * detached from the ipw->rxq. The driver 'processed' index is updated.
+ * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
+ * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
+ * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there
+ * were enough free buffers and RX_STALLED is set it is cleared.
+ *
+ *
+ * Driver sequence:
+ *
+ * ipw_rx_queue_alloc() Allocates rx_free
+ * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls
+ * ipw_rx_queue_restock
+ * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx
+ * queue, updates firmware pointers, and updates
+ * the WRITE index. If insufficient rx_free buffers
+ * are available, schedules ipw_rx_queue_replenish
+ *
+ * -- enable interrupts --
+ * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the
+ * READ INDEX, detaching the SKB from the pool.
+ * Moves the packet buffer from queue to rx_used.
+ * Calls ipw_rx_queue_restock to refill any empty
+ * slots.
+ * ...
+ *
+ */
+
+/*
+ * If there are slots in the RX queue that need to be restocked,
+ * and we have free pre-allocated buffers, fill the ranks as much
+ * as we can pulling from rx_free.
+ *
+ * This moves the 'write' index forward to catch up with 'processed', and
+ * also updates the memory address in the firmware to reference the new
+ * target buffer.
+ */
+static void ipw_rx_queue_restock(struct ipw_priv *priv)
+{
+ struct ipw_rx_queue *rxq = priv->rxq;
+ struct list_head *element;
+ struct ipw_rx_mem_buffer *rxb;
+ unsigned long flags;
+ int write;
+
+ spin_lock_irqsave(&rxq->lock, flags);
+ write = rxq->write;
+ while ((rxq->write != rxq->processed) && (rxq->free_count)) {
+ element = rxq->rx_free.next;
+ rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
+ list_del(element);
+
+ ipw_write32(priv, CX2_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
+ rxb->dma_addr);
+ rxq->queue[rxq->write] = rxb;
+ rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
+ rxq->free_count--;
+ }
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ /* If the pre-allocated buffer pool is dropping low, schedule to
+ * refill it */
+ if (rxq->free_count <= RX_LOW_WATERMARK)
+ queue_work(priv->workqueue, &priv->rx_replenish);
+
+ /* If we've added more space for the firmware to place data, tell it */
+ if (write != rxq->write)
+ ipw_write32(priv, CX2_RX_WRITE_INDEX, rxq->write);
+}
+
+/*
+ * Move all used packet from rx_used to rx_free, allocating a new SKB for each.
+ * Also restock the Rx queue via ipw_rx_queue_restock.
+ *
+ * This is called as a scheduled work item (except for during intialization)
+ */
+static void ipw_rx_queue_replenish(void *data)
+{
+ struct ipw_priv *priv = data;
+ struct ipw_rx_queue *rxq = priv->rxq;
+ struct list_head *element;
+ struct ipw_rx_mem_buffer *rxb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rxq->lock, flags);
+ while (!list_empty(&rxq->rx_used)) {
+ element = rxq->rx_used.next;
+ rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
+ rxb->skb = alloc_skb(CX2_RX_BUF_SIZE, GFP_ATOMIC);
+ if (!rxb->skb) {
+ printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
+ priv->net_dev->name);
+ /* We don't reschedule replenish work here -- we will
+ * call the restock method and if it still needs
+ * more buffers it will schedule replenish */
+ break;
+ }
+ list_del(element);
+
+ rxb->rxb = (struct ipw_rx_buffer *)rxb->skb->data;
+ rxb->dma_addr = pci_map_single(
+ priv->pci_dev, rxb->skb->data, CX2_RX_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+
+ list_add_tail(&rxb->list, &rxq->rx_free);
+ rxq->free_count++;
+ }
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ ipw_rx_queue_restock(priv);
+}
+
+/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
+ * If an SKB has been detached, the POOL needs to have it's SKB set to NULL
+ * This free routine walks the list of POOL entries and if SKB is set to
+ * non NULL it is unmapped and freed
+ */
+static void ipw_rx_queue_free(struct ipw_priv *priv,
+ struct ipw_rx_queue *rxq)
+{
+ int i;
+
+ if (!rxq)
+ return;
+
+ for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
+ if (rxq->pool[i].skb != NULL) {
+ pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
+ CX2_RX_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(rxq->pool[i].skb);
+ }
+ }
+
+ kfree(rxq);
+}
+
+static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
+{
+ struct ipw_rx_queue *rxq;
+ int i;
+
+ rxq = (struct ipw_rx_queue *)kmalloc(sizeof(*rxq), GFP_KERNEL);
+ memset(rxq, 0, sizeof(*rxq));
+ spin_lock_init(&rxq->lock);
+ INIT_LIST_HEAD(&rxq->rx_free);
+ INIT_LIST_HEAD(&rxq->rx_used);
+
+ /* Fill the rx_used queue with _all_ of the Rx buffers */
+ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
+ list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+
+ /* Set us so that we have processed and used all buffers, but have
+ * not restocked the Rx queue with fresh buffers */
+ rxq->read = rxq->write = 0;
+ rxq->processed = RX_QUEUE_SIZE - 1;
+ rxq->free_count = 0;
+
+ return rxq;
+}
+
+static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
+{
+ rate &= ~IEEE80211_BASIC_RATE_MASK;
+ if (ieee_mode == IEEE_A) {
+ switch (rate) {
+ case IEEE80211_OFDM_RATE_6MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ?
+ 1 : 0;
+ case IEEE80211_OFDM_RATE_9MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ?
+ 1 : 0;
+ case IEEE80211_OFDM_RATE_12MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ?
+ 1 : 0;
+ case IEEE80211_OFDM_RATE_18MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ?
+ 1 : 0;
+ case IEEE80211_OFDM_RATE_24MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ?
+ 1 : 0;
+ case IEEE80211_OFDM_RATE_36MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ?
+ 1 : 0;
+ case IEEE80211_OFDM_RATE_48MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ?
+ 1 : 0;
+ case IEEE80211_OFDM_RATE_54MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ?
+ 1 : 0;
+ default:
+ return 0;
+ }
+ }
+
+ /* B and G mixed */
+ switch (rate) {
+ case IEEE80211_CCK_RATE_1MB:
+ return priv->rates_mask & IEEE80211_CCK_RATE_1MB_MASK ? 1 : 0;
+ case IEEE80211_CCK_RATE_2MB:
+ return priv->rates_mask & IEEE80211_CCK_RATE_2MB_MASK ? 1 : 0;
+ case IEEE80211_CCK_RATE_5MB:
+ return priv->rates_mask & IEEE80211_CCK_RATE_5MB_MASK ? 1 : 0;
+ case IEEE80211_CCK_RATE_11MB:
+ return priv->rates_mask & IEEE80211_CCK_RATE_11MB_MASK ? 1 : 0;
+ }
+
+ /* If we are limited to B modulations, bail at this point */
+ if (ieee_mode == IEEE_B)
+ return 0;
+
+ /* G */
+ switch (rate) {
+ case IEEE80211_OFDM_RATE_6MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_9MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_12MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_18MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_24MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_36MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_48MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_54MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
+ }
+
+ return 0;
+}
+
+static int ipw_compatible_rates(struct ipw_priv *priv,
+ const struct ieee80211_network *network,
+ struct ipw_supported_rates *rates)
+{
+ int num_rates, i;
+
+ memset(rates, 0, sizeof(*rates));
+ num_rates = min(network->rates_len, (u8)IPW_MAX_RATES);
+ rates->num_rates = 0;
+ for (i = 0; i < num_rates; i++) {
+ if (!ipw_is_rate_in_mask(priv, network->mode, network->rates[i])) {
+ IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
+ network->rates[i], priv->rates_mask);
+ continue;
+ }
+
+ rates->supported_rates[rates->num_rates++] = network->rates[i];
+ }
+
+ num_rates = min(network->rates_ex_len, (u8)(IPW_MAX_RATES - num_rates));
+ for (i = 0; i < num_rates; i++) {
+ if (!ipw_is_rate_in_mask(priv, network->mode, network->rates_ex[i])) {
+ IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
+ network->rates_ex[i], priv->rates_mask);
+ continue;
+ }
+
+ rates->supported_rates[rates->num_rates++] = network->rates_ex[i];
+ }
+
+ return rates->num_rates;
+}
+
+static inline void ipw_copy_rates(struct ipw_supported_rates *dest,
+ const struct ipw_supported_rates *src)
+{
+ u8 i;
+ for (i = 0; i < src->num_rates; i++)
+ dest->supported_rates[i] = src->supported_rates[i];
+ dest->num_rates = src->num_rates;
+}
+
+/* TODO: Look at sniffed packets in the air to determine if the basic rate
+ * mask should ever be used -- right now all callers to add the scan rates are
+ * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */
+static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
+ u8 modulation, u32 rate_mask)
+{
+ u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
+ IEEE80211_BASIC_RATE_MASK : 0;
+
+ if (rate_mask & IEEE80211_CCK_RATE_1MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
+
+ if (rate_mask & IEEE80211_CCK_RATE_2MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
+
+ if (rate_mask & IEEE80211_CCK_RATE_5MB_MASK)
+ rates->supported_rates[rates->num_rates++] = basic_mask |
+ IEEE80211_CCK_RATE_5MB;
+
+ if (rate_mask & IEEE80211_CCK_RATE_11MB_MASK)
+ rates->supported_rates[rates->num_rates++] = basic_mask |
+ IEEE80211_CCK_RATE_11MB;
+}
+
+static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
+ u8 modulation, u32 rate_mask)
+{
+ u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
+ IEEE80211_BASIC_RATE_MASK : 0;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_6MB_MASK)
+ rates->supported_rates[rates->num_rates++] = basic_mask |
+ IEEE80211_OFDM_RATE_6MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_9MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_OFDM_RATE_9MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_12MB_MASK)
+ rates->supported_rates[rates->num_rates++] = basic_mask |
+ IEEE80211_OFDM_RATE_12MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_18MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_OFDM_RATE_18MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_24MB_MASK)
+ rates->supported_rates[rates->num_rates++] = basic_mask |
+ IEEE80211_OFDM_RATE_24MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_36MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_OFDM_RATE_36MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_48MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_OFDM_RATE_48MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_54MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_OFDM_RATE_54MB;
+}
+
+struct ipw_network_match {
+ struct ieee80211_network *network;
+ struct ipw_supported_rates rates;
+};
+
+static int ipw_best_network(
+ struct ipw_priv *priv,
+ struct ipw_network_match *match,
+ struct ieee80211_network *network,
+ int roaming)
+{
+ struct ipw_supported_rates rates;
+
+ /* Verify that this network's capability is compatible with the
+ * current mode (AdHoc or Infrastructure) */
+ if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
+ !(network->capability & WLAN_CAPABILITY_ESS)) ||
+ (priv->ieee->iw_mode == IW_MODE_ADHOC &&
+ !(network->capability & WLAN_CAPABILITY_IBSS))) {
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded due to "
+ "capability mismatch.\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid));
+ return 0;
+ }
+
+ /* If we do not have an ESSID for this AP, we can not associate with
+ * it */
+ if (network->flags & NETWORK_EMPTY_ESSID) {
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+ "because of hidden ESSID.\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid));
+ return 0;
+ }
+
+ if (unlikely(roaming)) {
+ /* If we are roaming, then ensure check if this is a valid
+ * network to try and roam to */
+ if ((network->ssid_len != match->network->ssid_len) ||
+ memcmp(network->ssid, match->network->ssid,
+ network->ssid_len)) {
+ IPW_DEBUG_ASSOC("Netowrk '%s (" MAC_FMT ")' excluded "
+ "because of non-network ESSID.\n",
+ escape_essid(network->ssid,
+ network->ssid_len),
+ MAC_ARG(network->bssid));
+ return 0;
+ }
+ } else {
+ /* If an ESSID has been configured then compare the broadcast
+ * ESSID to ours */
+ if ((priv->config & CFG_STATIC_ESSID) &&
+ ((network->ssid_len != priv->essid_len) ||
+ memcmp(network->ssid, priv->essid,
+ min(network->ssid_len, priv->essid_len)))) {
+ char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
+ strncpy(escaped, escape_essid(
+ network->ssid, network->ssid_len),
+ sizeof(escaped));
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+ "because of ESSID mismatch: '%s'.\n",
+ escaped, MAC_ARG(network->bssid),
+ escape_essid(priv->essid, priv->essid_len));
+ return 0;
+ }
+ }
+
+ /* If the old network rate is better than this one, don't bother
+ * testing everything else. */
+ if (match->network && match->network->stats.rssi >
+ network->stats.rssi) {
+ char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
+ strncpy(escaped,
+ escape_essid(network->ssid, network->ssid_len),
+ sizeof(escaped));
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded because "
+ "'%s (" MAC_FMT ")' has a stronger signal.\n",
+ escaped, MAC_ARG(network->bssid),
+ escape_essid(match->network->ssid,
+ match->network->ssid_len),
+ MAC_ARG(match->network->bssid));
+ return 0;
+ }
+
+ /* If this network has already had an association attempt within the
+ * last 3 seconds, do not try and associate again... */
+ if (network->last_associate &&
+ time_after(network->last_associate + (HZ * 5UL), jiffies)) {
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+ "because of storming (%lu since last "
+ "assoc attempt).\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid),
+ (jiffies - network->last_associate) / HZ);
+ return 0;
+ }
+
+ /* Now go through and see if the requested network is valid... */
+ if (priv->ieee->scan_age != 0 &&
+ jiffies - network->last_scanned > priv->ieee->scan_age) {
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+ "because of age: %lums.\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid),
+ (jiffies - network->last_scanned) / (HZ / 100));
+ return 0;
+ }
+
+ if ((priv->config & CFG_STATIC_CHANNEL) &&
+ (network->channel != priv->channel)) {
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+ "because of channel mismatch: %d != %d.\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid),
+ network->channel, priv->channel);
+ return 0;
+ }
+
+ /* Verify privacy compatability */
+ if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
+ ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+ "because of privacy mismatch: %s != %s.\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid),
+ priv->capability & CAP_PRIVACY_ON ? "on" :
+ "off",
+ network->capability &
+ WLAN_CAPABILITY_PRIVACY ?"on" : "off");
+ return 0;
+ }
+
+ if ((priv->config & CFG_STATIC_BSSID) &&
+ memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+ "because of BSSID mismatch: " MAC_FMT ".\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid),
+ MAC_ARG(priv->bssid));
+ return 0;
+ }
+
+ /* Filter out any incompatible freq / mode combinations */
+ if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+ "because of invalid frequency/mode "
+ "combination.\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid));
+ return 0;
+ }
+
+ ipw_compatible_rates(priv, network, &rates);
+ if (rates.num_rates == 0) {
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+ "because of no compatible rates.\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid));
+ return 0;
+ }
+
+ /* TODO: Perform any further minimal comparititive tests. We do not
+ * want to put too much policy logic here; intelligent scan selection
+ * should occur within a generic IEEE 802.11 user space tool. */
+
+ /* Set up 'new' AP to this network */
+ ipw_copy_rates(&match->rates, &rates);
+ match->network = network;
+
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' is a viable match.\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid));
+
+ return 1;
+}
+
+
+static void ipw_adhoc_create(struct ipw_priv *priv,
+ struct ieee80211_network *network)
+{
+ /*
+ * For the purposes of scanning, we can set our wireless mode
+ * to trigger scans across combinations of bands, but when it
+ * comes to creating a new ad-hoc network, we have tell the FW
+ * exactly which band to use.
+ *
+ * We also have the possibility of an invalid channel for the
+ * chossen band. Attempting to create a new ad-hoc network
+ * with an invalid channel for wireless mode will trigger a
+ * FW fatal error.
+ */
+ network->mode = is_valid_channel(priv->ieee->mode, priv->channel);
+ if (network->mode) {
+ network->channel = priv->channel;
+ } else {
+ IPW_WARNING("Overriding invalid channel\n");
+ if (priv->ieee->mode & IEEE_A) {
+ network->mode = IEEE_A;
+ priv->channel = band_a_active_channel[0];
+ } else if (priv->ieee->mode & IEEE_G) {
+ network->mode = IEEE_G;
+ priv->channel = band_b_active_channel[0];
+ } else {
+ network->mode = IEEE_B;
+ priv->channel = band_b_active_channel[0];
+ }
+ }
+
+ network->channel = priv->channel;
+ priv->config |= CFG_ADHOC_PERSIST;
+ ipw_create_bssid(priv, network->bssid);
+ network->ssid_len = priv->essid_len;
+ memcpy(network->ssid, priv->essid, priv->essid_len);
+ memset(&network->stats, 0, sizeof(network->stats));
+ network->capability = WLAN_CAPABILITY_IBSS;
+ if (priv->capability & CAP_PRIVACY_ON)
+ network->capability |= WLAN_CAPABILITY_PRIVACY;
+ network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
+ memcpy(network->rates, priv->rates.supported_rates,
+ network->rates_len);
+ network->rates_ex_len = priv->rates.num_rates - network->rates_len;
+ memcpy(network->rates_ex,
+ &priv->rates.supported_rates[network->rates_len],
+ network->rates_ex_len);
+ network->last_scanned = 0;
+ network->flags = 0;
+ network->last_associate = 0;
+ network->time_stamp[0] = 0;
+ network->time_stamp[1] = 0;
+ network->beacon_interval = 100; /* Default */
+ network->listen_interval = 10; /* Default */
+ network->atim_window = 0; /* Default */
+#ifdef CONFIG_IEEE80211_WPA
+ network->wpa_ie_len = 0;
+ network->rsn_ie_len = 0;
+#endif /* CONFIG_IEEE80211_WPA */
+}
+
+static void ipw_send_wep_keys(struct ipw_priv *priv)
+{
+ struct ipw_wep_key *key;
+ int i;
+ struct host_cmd cmd = {
+ .cmd = IPW_CMD_WEP_KEY,
+ .len = sizeof(*key)
+ };
+
+ key = (struct ipw_wep_key *)&cmd.param;
+ key->cmd_id = DINO_CMD_WEP_KEY;
+ key->seq_num = 0;
+
+ for (i = 0; i < 4; i++) {
+ key->key_index = i;
+ if (!(priv->sec.flags & (1 << i))) {
+ key->key_size = 0;
+ } else {
+ key->key_size = priv->sec.key_sizes[i];
+ memcpy(key->key, priv->sec.keys[i], key->key_size);
+ }
+
+ if (ipw_send_cmd(priv, &cmd)) {
+ IPW_ERROR("failed to send WEP_KEY command\n");
+ return;
+ }
+ }
+}
+
+static void ipw_adhoc_check(void *data)
+{
+ struct ipw_priv *priv = data;
+
+ if (priv->missed_adhoc_beacons++ > priv->missed_beacon_threshold &&
+ !(priv->config & CFG_ADHOC_PERSIST)) {
+ IPW_DEBUG_SCAN("Disassociating due to missed beacons\n");
+ ipw_remove_current_network(priv);
+ ipw_disassociate(priv);
+ return;
+ }
+
+ queue_delayed_work(priv->workqueue, &priv->adhoc_check,
+ priv->assoc_request.beacon_interval);
+}
+
+#ifdef CONFIG_IPW_DEBUG
+static void ipw_debug_config(struct ipw_priv *priv)
+{
+ IPW_DEBUG_INFO("Scan completed, no valid APs matched "
+ "[CFG 0x%08X]\n", priv->config);
+ if (priv->config & CFG_STATIC_CHANNEL)
+ IPW_DEBUG_INFO("Channel locked to %d\n",
+ priv->channel);
+ else
+ IPW_DEBUG_INFO("Channel unlocked.\n");
+ if (priv->config & CFG_STATIC_ESSID)
+ IPW_DEBUG_INFO("ESSID locked to '%s'\n",
+ escape_essid(priv->essid,
+ priv->essid_len));
+ else
+ IPW_DEBUG_INFO("ESSID unlocked.\n");
+ if (priv->config & CFG_STATIC_BSSID)
+ IPW_DEBUG_INFO("BSSID locked to %d\n", priv->channel);
+ else
+ IPW_DEBUG_INFO("BSSID unlocked.\n");
+ if (priv->capability & CAP_PRIVACY_ON)
+ IPW_DEBUG_INFO("PRIVACY on\n");
+ else
+ IPW_DEBUG_INFO("PRIVACY off\n");
+ IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
+}
+#else
+#define ipw_debug_config(x) do {} while (0)
+#endif
+
+static inline void ipw_set_fixed_rate(struct ipw_priv *priv,
+ struct ieee80211_network *network)
+{
+ /* TODO: Verify that this works... */
+ struct ipw_fixed_rate fr = {
+ .tx_rates = priv->rates_mask
+ };
+ u32 reg;
+ u16 mask = 0;
+
+ /* Identify 'current FW band' and match it with the fixed
+ * Tx rates */
+
+ switch (priv->ieee->freq_band) {
+ case IEEE80211_52GHZ_BAND: /* A only */
+ /* IEEE_A */
+ if (priv->rates_mask & ~IEEE80211_OFDM_RATES_MASK) {
+ /* Invalid fixed rate mask */
+ fr.tx_rates = 0;
+ break;
+ }
+
+ fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A;
+ break;
+
+ default: /* 2.4Ghz or Mixed */
+ /* IEEE_B */
+ if (network->mode == IEEE_B) {
+ if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) {
+ /* Invalid fixed rate mask */
+ fr.tx_rates = 0;
+ }
+ break;
+ }
+
+ /* IEEE_G */
+ if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK |
+ IEEE80211_OFDM_RATES_MASK)) {
+ /* Invalid fixed rate mask */
+ fr.tx_rates = 0;
+ break;
+ }
+
+ if (IEEE80211_OFDM_RATE_6MB_MASK & fr.tx_rates) {
+ mask |= (IEEE80211_OFDM_RATE_6MB_MASK >> 1);
+ fr.tx_rates &= ~IEEE80211_OFDM_RATE_6MB_MASK;
+ }
+
+ if (IEEE80211_OFDM_RATE_9MB_MASK & fr.tx_rates) {
+ mask |= (IEEE80211_OFDM_RATE_9MB_MASK >> 1);
+ fr.tx_rates &= ~IEEE80211_OFDM_RATE_9MB_MASK;
+ }
+
+ if (IEEE80211_OFDM_RATE_12MB_MASK & fr.tx_rates) {
+ mask |= (IEEE80211_OFDM_RATE_12MB_MASK >> 1);
+ fr.tx_rates &= ~IEEE80211_OFDM_RATE_12MB_MASK;
+ }
+
+ fr.tx_rates |= mask;
+ break;
+ }
+
+ reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
+ ipw_write_reg32(priv, reg, *(u32*)&fr);
+}
+
+static int ipw_associate_network(struct ipw_priv *priv,
+ struct ieee80211_network *network,
+ struct ipw_supported_rates *rates,
+ int roaming)
+{
+ int err;
+
+ if (priv->config & CFG_FIXED_RATE)
+ ipw_set_fixed_rate(priv, network);
+
+ if (!(priv->config & CFG_STATIC_ESSID)) {
+ priv->essid_len = min(network->ssid_len,
+ (u8)IW_ESSID_MAX_SIZE);
+ memcpy(priv->essid, network->ssid, priv->essid_len);
+ }
+
+ network->last_associate = jiffies;
+
+ memset(&priv->assoc_request, 0, sizeof(priv->assoc_request));
+ priv->assoc_request.channel = network->channel;
+ if ((priv->capability & CAP_PRIVACY_ON) &&
+ (priv->capability & CAP_SHARED_KEY)) {
+ priv->assoc_request.auth_type = AUTH_SHARED_KEY;
+ priv->assoc_request.auth_key = priv->sec.active_key;
+ } else {
+ priv->assoc_request.auth_type = AUTH_OPEN;
+ priv->assoc_request.auth_key = 0;
+ }
+
+ if (priv->capability & CAP_PRIVACY_ON)
+ ipw_send_wep_keys(priv);
+
+ /*
+ * It is valid for our ieee device to support multiple modes, but
+ * when it comes to associating to a given network we have to choose
+ * just one mode.
+ */
+ if (network->mode & priv->ieee->mode & IEEE_A)
+ priv->assoc_request.ieee_mode = IPW_A_MODE;
+ else if (network->mode & priv->ieee->mode & IEEE_G)
+ priv->assoc_request.ieee_mode = IPW_G_MODE;
+ else if (network->mode & priv->ieee->mode & IEEE_B)
+ priv->assoc_request.ieee_mode = IPW_B_MODE;
+
+ IPW_DEBUG_ASSOC("%sssocation attempt: '%s', channel %d, "
+ "802.11%c [%d], enc=%s%s%s%c%c\n",
+ roaming ? "Rea" : "A",
+ escape_essid(priv->essid, priv->essid_len),
+ network->channel,
+ ipw_modes[priv->assoc_request.ieee_mode],
+ rates->num_rates,
+ priv->capability & CAP_PRIVACY_ON ? "on " : "off",
+ priv->capability & CAP_PRIVACY_ON ?
+ (priv->capability & CAP_SHARED_KEY ? "(shared)" :
+ "(open)") : "",
+ priv->capability & CAP_PRIVACY_ON ? " key=" : "",
+ priv->capability & CAP_PRIVACY_ON ?
+ '1' + priv->sec.active_key : '.',
+ priv->capability & CAP_PRIVACY_ON ?
+ '.' : ' ');
+
+ priv->assoc_request.beacon_interval = network->beacon_interval;
+ if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
+ (network->time_stamp[0] == 0) &&
+ (network->time_stamp[1] == 0)) {
+ priv->assoc_request.assoc_type = HC_IBSS_START;
+ priv->assoc_request.assoc_tsf_msw = 0;
+ priv->assoc_request.assoc_tsf_lsw = 0;
+ } else {
+ if (unlikely(roaming))
+ priv->assoc_request.assoc_type = HC_REASSOCIATE;
+ else
+ priv->assoc_request.assoc_type = HC_ASSOCIATE;
+ priv->assoc_request.assoc_tsf_msw = network->time_stamp[1];
+ priv->assoc_request.assoc_tsf_lsw = network->time_stamp[0];
+ }
+
+ memcpy(&priv->assoc_request.bssid, network->bssid, ETH_ALEN);
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN);
+ priv->assoc_request.atim_window = network->atim_window;
+ } else {
+ memcpy(&priv->assoc_request.dest, network->bssid,
+ ETH_ALEN);
+ priv->assoc_request.atim_window = 0;
+ }
+
+ priv->assoc_request.capability = network->capability;
+ priv->assoc_request.listen_interval = network->listen_interval;
+
+ err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
+ return err;
+ }
+
+ rates->ieee_mode = priv->assoc_request.ieee_mode;
+ rates->purpose = IPW_RATE_CONNECT;
+ ipw_send_supported_rates(priv, rates);
+
+ if (priv->assoc_request.ieee_mode == IPW_G_MODE)
+ priv->sys_config.dot11g_auto_detection = 1;
+ else
+ priv->sys_config.dot11g_auto_detection = 0;
+ err = ipw_send_system_config(priv, &priv->sys_config);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
+ return err;
+ }
+
+ IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi);
+ err = ipw_set_sensitivity(priv, network->stats.rssi);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send associate command failed.\n");
+ return err;
+ }
+
+ /*
+ * If preemption is enabled, it is possible for the association
+ * to complete before we return from ipw_send_associate. Therefore
+ * we have to be sure and update our priviate data first.
+ */
+ priv->channel = network->channel;
+ memcpy(priv->bssid, network->bssid, ETH_ALEN);
+ priv->status |= STATUS_ASSOCIATING;
+ priv->status &= ~STATUS_SECURITY_UPDATED;
+
+ priv->assoc_network = network;
+
+ err = ipw_send_associate(priv, &priv->assoc_request);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send associate command failed.\n");
+ return err;
+ }
+
+ IPW_DEBUG(IPW_DL_STATE, "associating: '%s' " MAC_FMT " \n",
+ escape_essid(priv->essid, priv->essid_len),
+ MAC_ARG(priv->bssid));
+
+ return 0;
+}
+
+static void ipw_roam(void *data)
+{
+ struct ipw_priv *priv = data;
+ struct ieee80211_network *network = NULL;
+ struct ipw_network_match match = {
+ .network = priv->assoc_network
+ };
+
+ /* The roaming process is as follows:
+ *
+ * 1. Missed beacon threshold triggers the roaming process by
+ * setting the status ROAM bit and requesting a scan.
+ * 2. When the scan completes, it schedules the ROAM work
+ * 3. The ROAM work looks at all of the known networks for one that
+ * is a better network than the currently associated. If none
+ * found, the ROAM process is over (ROAM bit cleared)
+ * 4. If a better network is found, a disassociation request is
+ * sent.
+ * 5. When the disassociation completes, the roam work is again
+ * scheduled. The second time through, the driver is no longer
+ * associated, and the newly selected network is sent an
+ * association request.
+ * 6. At this point ,the roaming process is complete and the ROAM
+ * status bit is cleared.
+ */
+
+ /* If we are no longer associated, and the roaming bit is no longer
+ * set, then we are not actively roaming, so just return */
+ if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING)))
+ return;
+
+ if (priv->status & STATUS_ASSOCIATED) {
+ /* First pass through ROAM process -- look for a better
+ * network */
+ u8 rssi = priv->assoc_network->stats.rssi;
+ priv->assoc_network->stats.rssi = -128;
+ list_for_each_entry(network, &priv->ieee->network_list, list) {
+ if (network != priv->assoc_network)
+ ipw_best_network(priv, &match, network, 1);
+ }
+ priv->assoc_network->stats.rssi = rssi;
+
+ if (match.network == priv->assoc_network) {
+ IPW_DEBUG_ASSOC("No better APs in this network to "
+ "roam to.\n");
+ priv->status &= ~STATUS_ROAMING;
+ ipw_debug_config(priv);
+ return;
+ }
+
+ ipw_send_disassociate(priv, 1);
+ priv->assoc_network = match.network;
+
+ return;
+ }
+
+ /* Second pass through ROAM process -- request association */
+ ipw_compatible_rates(priv, priv->assoc_network, &match.rates);
+ ipw_associate_network(priv, priv->assoc_network, &match.rates, 1);
+ priv->status &= ~STATUS_ROAMING;
+}
+
+static void ipw_associate(void *data)
+{
+ struct ipw_priv *priv = data;
+
+ struct ieee80211_network *network = NULL;
+ struct ipw_network_match match = {
+ .network = NULL
+ };
+ struct ipw_supported_rates *rates;
+ struct list_head *element;
+
+ if (!(priv->config & CFG_ASSOCIATE) &&
+ !(priv->config & (CFG_STATIC_ESSID |
+ CFG_STATIC_CHANNEL |
+ CFG_STATIC_BSSID))) {
+ IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
+ return;
+ }
+
+ list_for_each_entry(network, &priv->ieee->network_list, list)
+ ipw_best_network(priv, &match, network, 0);
+
+ network = match.network;
+ rates = &match.rates;
+
+ if (network == NULL &&
+ priv->ieee->iw_mode == IW_MODE_ADHOC &&
+ priv->config & CFG_ADHOC_CREATE &&
+ priv->config & CFG_STATIC_ESSID &&
+ !list_empty(&priv->ieee->network_free_list)) {
+ element = priv->ieee->network_free_list.next;
+ network = list_entry(element, struct ieee80211_network,
+ list);
+ ipw_adhoc_create(priv, network);
+ rates = &priv->rates;
+ list_del(element);
+ list_add_tail(&network->list, &priv->ieee->network_list);
+ }
+
+ /* If we reached the end of the list, then we don't have any valid
+ * matching APs */
+ if (!network) {
+ ipw_debug_config(priv);
+
+ queue_delayed_work(priv->workqueue, &priv->request_scan,
+ SCAN_INTERVAL);
+
+ return;
+ }
+
+ ipw_associate_network(priv, network, rates, 0);
+}
+
+static inline void ipw_handle_data_packet(struct ipw_priv *priv,
+ struct ipw_rx_mem_buffer *rxb,
+ struct ieee80211_rx_stats *stats)
+{
+ struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
+
+ /* We received data from the HW, so stop the watchdog */
+ priv->net_dev->trans_start = jiffies;
+
+ /* We only process data packets if the
+ * interface is open */
+ if (unlikely((pkt->u.frame.length + IPW_RX_FRAME_SIZE) >
+ skb_tailroom(rxb->skb))) {
+ priv->ieee->stats.rx_errors++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
+ return;
+ } else if (unlikely(!netif_running(priv->net_dev))) {
+ priv->ieee->stats.rx_dropped++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+ return;
+ }
+
+ /* Advance skb->data to the start of the actual payload */
+ skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
+
+ /* Set the size of the skb to the size of the frame */
+ skb_put(rxb->skb, pkt->u.frame.length);
+
+ IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
+
+ if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
+ priv->ieee->stats.rx_errors++;
+ else /* ieee80211_rx succeeded, so it now owns the SKB */
+ rxb->skb = NULL;
+}
+
+
+/*
+ * Main entry function for recieving a packet with 80211 headers. This
+ * should be called when ever the FW has notified us that there is a new
+ * skb in the recieve queue.
+ */
+static void ipw_rx(struct ipw_priv *priv)
+{
+ struct ipw_rx_mem_buffer *rxb;
+ struct ipw_rx_packet *pkt;
+ struct ieee80211_hdr *header;
+ u32 r, w, i;
+ u8 network_packet;
+
+ r = ipw_read32(priv, CX2_RX_READ_INDEX);
+ w = ipw_read32(priv, CX2_RX_WRITE_INDEX);
+ i = (priv->rxq->processed + 1) % RX_QUEUE_SIZE;
+
+ while (i != r) {
+ rxb = priv->rxq->queue[i];
+#ifdef CONFIG_IPW_DEBUG
+ if (unlikely(rxb == NULL)) {
+ printk(KERN_CRIT "Queue not allocated!\n");
+ break;
+ }
+#endif
+ priv->rxq->queue[i] = NULL;
+
+ pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
+ CX2_RX_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+
+ pkt = (struct ipw_rx_packet *)rxb->skb->data;
+ IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
+ pkt->header.message_type,
+ pkt->header.rx_seq_num,
+ pkt->header.control_bits);
+
+ switch (pkt->header.message_type) {
+ case RX_FRAME_TYPE: /* 802.11 frame */ {
+ struct ieee80211_rx_stats stats = {
+ .rssi = pkt->u.frame.rssi_dbm -
+ IPW_RSSI_TO_DBM,
+ .signal = pkt->u.frame.signal,
+ .rate = pkt->u.frame.rate,
+ .mac_time = jiffies,
+ .received_channel =
+ pkt->u.frame.received_channel,
+ .freq = (pkt->u.frame.control & (1<<0)) ?
+ IEEE80211_24GHZ_BAND : IEEE80211_52GHZ_BAND,
+ .len = pkt->u.frame.length,
+ };
+
+ if (stats.rssi != 0)
+ stats.mask |= IEEE80211_STATMASK_RSSI;
+ if (stats.signal != 0)
+ stats.mask |= IEEE80211_STATMASK_SIGNAL;
+ if (stats.rate != 0)
+ stats.mask |= IEEE80211_STATMASK_RATE;
+
+ priv->rx_packets++;
+
+#ifdef CONFIG_IPW_PROMISC
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ ipw_handle_data_packet(priv, rxb, &stats);
+ break;
+ }
+#endif
+
+ header = (struct ieee80211_hdr *)(rxb->skb->data +
+ IPW_RX_FRAME_SIZE);
+ /* TODO: Check Ad-Hoc dest/source and make sure
+ * that we are actually parsing these packets
+ * correctly -- we should probably use the
+ * frame control of the packet and disregard
+ * the current iw_mode */
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ network_packet =
+ !memcmp(header->addr1,
+ priv->net_dev->dev_addr,
+ ETH_ALEN) ||
+ !memcmp(header->addr3,
+ priv->bssid, ETH_ALEN) ||
+ is_broadcast_ether_addr(header->addr1) ||
+ is_multicast_ether_addr(header->addr1);
+ break;
+
+ case IW_MODE_INFRA:
+ default:
+ network_packet =
+ !memcmp(header->addr3,
+ priv->bssid, ETH_ALEN) ||
+ !memcmp(header->addr1,
+ priv->net_dev->dev_addr,
+ ETH_ALEN) ||
+ is_broadcast_ether_addr(header->addr1) ||
+ is_multicast_ether_addr(header->addr1);
+ break;
+ }
+
+ if (network_packet && priv->assoc_network) {
+ priv->assoc_network->stats.rssi = stats.rssi;
+ average_add(&priv->average_rssi,
+ stats.rssi);
+ priv->last_rx_rssi = stats.rssi;
+ }
+
+ IPW_DEBUG_RX("Frame: len=%u\n", pkt->u.frame.length);
+
+ if (pkt->u.frame.length < frame_hdr_len(header)) {
+ IPW_DEBUG_DROP("Received packet is too small. "
+ "Dropping.\n");
+ priv->ieee->stats.rx_errors++;
+ priv->wstats.discard.misc++;
+ break;
+ }
+
+ switch (WLAN_FC_GET_TYPE(header->frame_ctl)) {
+ case IEEE80211_FTYPE_MGMT:
+ ieee80211_rx_mgt(priv->ieee, header, &stats);
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC &&
+ ((WLAN_FC_GET_STYPE(header->frame_ctl) ==
+ IEEE80211_STYPE_PROBE_RESP) ||
+ (WLAN_FC_GET_STYPE(header->frame_ctl) ==
+ IEEE80211_STYPE_BEACON)) &&
+ !memcmp(header->addr3, priv->bssid, ETH_ALEN))
+ ipw_add_station(priv, header->addr2);
+ break;
+
+ case IEEE80211_FTYPE_CTL:
+ break;
+
+ case IEEE80211_FTYPE_DATA:
+ if (network_packet)
+ ipw_handle_data_packet(priv, rxb, &stats);
+ else
+ IPW_DEBUG_DROP("Dropping: " MAC_FMT
+ ", " MAC_FMT ", " MAC_FMT "\n",
+ MAC_ARG(header->addr1), MAC_ARG(header->addr2),
+ MAC_ARG(header->addr3));
+ break;
+ }
+ break;
+ }
+
+ case RX_HOST_NOTIFICATION_TYPE: {
+ IPW_DEBUG_RX("Notification: subtype=%02X flags=%02X size=%d\n",
+ pkt->u.notification.subtype,
+ pkt->u.notification.flags,
+ pkt->u.notification.size);
+ ipw_rx_notification(priv, &pkt->u.notification);
+ break;
+ }
+
+ default:
+ IPW_DEBUG_RX("Bad Rx packet of type %d\n",
+ pkt->header.message_type);
+ break;
+ }
+
+ /* For now we just don't re-use anything. We can tweak this
+ * later to try and re-use notification packets and SKBs that
+ * fail to Rx correctly */
+ if (rxb->skb != NULL) {
+ dev_kfree_skb_any(rxb->skb);
+ rxb->skb = NULL;
+ }
+
+ pci_unmap_single(priv->pci_dev, rxb->dma_addr,
+ CX2_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ list_add_tail(&rxb->list, &priv->rxq->rx_used);
+
+ i = (i + 1) % RX_QUEUE_SIZE;
+ }
+
+ /* Backtrack one entry */
+ priv->rxq->processed = (i ? i : RX_QUEUE_SIZE) - 1;
+
+ ipw_rx_queue_restock(priv);
+}
+
+static void ipw_abort_scan(struct ipw_priv *priv)
+{
+ int err;
+
+ if (priv->status & STATUS_SCAN_ABORTING) {
+ IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
+ return;
+ }
+ priv->status |= STATUS_SCAN_ABORTING;
+
+ err = ipw_send_scan_abort(priv);
+ if (err)
+ IPW_DEBUG_HC("Request to abort scan failed.\n");
+}
+
+static int ipw_request_scan(struct ipw_priv *priv)
+{
+ struct ipw_scan_request_ext scan;
+ int channel_index = 0;
+ int i, err, scan_type;
+
+ if (priv->status & STATUS_EXIT_PENDING) {
+ IPW_DEBUG_SCAN("Aborting scan due to device shutdown\n");
+ priv->status |= STATUS_SCAN_PENDING;
+ return 0;
+ }
+
+ if (priv->status & STATUS_SCANNING) {
+ IPW_DEBUG_HC("Concurrent scan requested. Aborting first.\n");
+ priv->status |= STATUS_SCAN_PENDING;
+ ipw_abort_scan(priv);
+ return 0;
+ }
+
+ if (priv->status & STATUS_SCAN_ABORTING) {
+ IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
+ priv->status |= STATUS_SCAN_PENDING;
+ return 0;
+ }
+
+ if (priv->status & STATUS_RF_KILL_MASK) {
+ IPW_DEBUG_HC("Aborting scan due to RF Kill activation\n");
+ priv->status |= STATUS_SCAN_PENDING;
+ return 0;
+ }
+
+ memset(&scan, 0, sizeof(scan));
+
+ scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] = 20;
+ scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] = 20;
+ scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = 20;
+
+ scan.full_scan_index = ieee80211_get_scans(priv->ieee);
+ /* If we are roaming, then make this a directed scan for the current
+ * network. Otherwise, ensure that every other scan is a fast
+ * channel hop scan */
+ if ((priv->status & STATUS_ROAMING) || (
+ !(priv->status & STATUS_ASSOCIATED) &&
+ (priv->config & CFG_STATIC_ESSID) &&
+ (scan.full_scan_index % 2))) {
+ err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
+ return err;
+ }
+
+ scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
+ } else {
+ scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
+ }
+
+ if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
+ int start = channel_index;
+ for (i = 0; i < MAX_A_CHANNELS; i++) {
+ if (band_a_active_channel[i] == 0)
+ break;
+ if ((priv->status & STATUS_ASSOCIATED) &&
+ band_a_active_channel[i] == priv->channel)
+ continue;
+ channel_index++;
+ scan.channels_list[channel_index] =
+ band_a_active_channel[i];
+ ipw_set_scan_type(&scan, channel_index, scan_type);
+ }
+
+ if (start != channel_index) {
+ scan.channels_list[start] = (u8)(IPW_A_MODE << 6) |
+ (channel_index - start);
+ channel_index++;
+ }
+ }
+
+ if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
+ int start = channel_index;
+ for (i = 0; i < MAX_B_CHANNELS; i++) {
+ if (band_b_active_channel[i] == 0)
+ break;
+ if ((priv->status & STATUS_ASSOCIATED) &&
+ band_b_active_channel[i] == priv->channel)
+ continue;
+ channel_index++;
+ scan.channels_list[channel_index] =
+ band_b_active_channel[i];
+ ipw_set_scan_type(&scan, channel_index, scan_type);
+ }
+
+ if (start != channel_index) {
+ scan.channels_list[start] = (u8)(IPW_B_MODE << 6) |
+ (channel_index - start);
+ }
+ }
+
+ err = ipw_send_scan_request_ext(priv, &scan);
+ if (err) {
+ IPW_DEBUG_HC("Sending scan command failed: %08X\n",
+ err);
+ return -EIO;
+ }
+
+ priv->status |= STATUS_SCANNING;
+ priv->status &= ~STATUS_SCAN_PENDING;
+
+ return 0;
+}
+
+/*
+ * This file defines the Wireless Extension handlers. It does not
+ * define any methods of hardware manipulation and relies on the
+ * functions defined in ipw_main to provide the HW interaction.
+ *
+ * The exception to this is the use of the ipw_get_ordinal()
+ * function used to poll the hardware vs. making unecessary calls.
+ *
+ */
+
+static int ipw_wx_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ if (!(priv->status & STATUS_ASSOCIATED))
+ strcpy(wrqu->name, "unassociated");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c",
+ ipw_modes[priv->assoc_request.ieee_mode]);
+ IPW_DEBUG_WX("Name: %s\n", wrqu->name);
+ return 0;
+}
+
+static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
+{
+ if (channel == 0) {
+ IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
+ priv->config &= ~CFG_STATIC_CHANNEL;
+ if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED |
+ STATUS_ASSOCIATING))) {
+ IPW_DEBUG_ASSOC("Attempting to associate with new "
+ "parameters.\n");
+ ipw_associate(priv);
+ }
+
+ return 0;
+ }
+
+ priv->config |= CFG_STATIC_CHANNEL;
+
+ if (priv->channel == channel) {
+ IPW_DEBUG_INFO(
+ "Request to set channel to current value (%d)\n",
+ channel);
+ return 0;
+ }
+
+ IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
+ priv->channel = channel;
+
+ /* If we are currently associated, or trying to associate
+ * then see if this is a new channel (causing us to disassociate) */
+ if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+ IPW_DEBUG_ASSOC("Disassociating due to channel change.\n");
+ ipw_disassociate(priv);
+ } else {
+ ipw_associate(priv);
+ }
+
+ return 0;
+}
+
+static int ipw_wx_set_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct iw_freq *fwrq = &wrqu->freq;
+
+ /* if setting by freq convert to channel */
+ if (fwrq->e == 1) {
+ if ((fwrq->m >= (int) 2.412e8 &&
+ fwrq->m <= (int) 2.487e8)) {
+ int f = fwrq->m / 100000;
+ int c = 0;
+
+ while ((c < REG_MAX_CHANNEL) &&
+ (f != ipw_frequencies[c]))
+ c++;
+
+ /* hack to fall through */
+ fwrq->e = 0;
+ fwrq->m = c + 1;
+ }
+ }
+
+ if (fwrq->e > 0 || fwrq->m > 1000)
+ return -EOPNOTSUPP;
+
+ IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
+ return ipw_set_channel(priv, (u8)fwrq->m);
+
+ return 0;
+}
+
+
+static int ipw_wx_get_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ wrqu->freq.e = 0;
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured CHANNEL then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_CHANNEL ||
+ priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))
+ wrqu->freq.m = priv->channel;
+ else
+ wrqu->freq.m = 0;
+
+ IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
+ return 0;
+}
+
+static int ipw_wx_set_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
+
+ if (wrqu->mode == priv->ieee->iw_mode)
+ return 0;
+
+ switch (wrqu->mode) {
+#ifdef CONFIG_IPW_PROMISC
+ case IW_MODE_MONITOR:
+#endif
+ case IW_MODE_ADHOC:
+ case IW_MODE_INFRA:
+ break;
+ case IW_MODE_AUTO:
+ wrqu->mode = IW_MODE_INFRA;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_IPW_PROMISC
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+ priv->net_dev->type = ARPHRD_ETHER;
+
+ if (wrqu->mode == IW_MODE_MONITOR)
+ priv->net_dev->type = ARPHRD_IEEE80211;
+#endif /* CONFIG_IPW_PROMISC */
+
+#ifdef CONFIG_PM
+ /* Free the existing firmware and reset the fw_loaded
+ * flag so ipw_load() will bring in the new firmawre */
+ if (fw_loaded) {
+ fw_loaded = 0;
+ }
+
+ release_firmware(bootfw);
+ release_firmware(ucode);
+ release_firmware(firmware);
+ bootfw = ucode = firmware = NULL;
+#endif
+
+ priv->ieee->iw_mode = wrqu->mode;
+ ipw_adapter_restart(priv);
+
+ return err;
+}
+
+static int ipw_wx_get_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ wrqu->mode = priv->ieee->iw_mode;
+ IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
+
+ return 0;
+}
+
+
+#define DEFAULT_RTS_THRESHOLD 2304U
+#define MIN_RTS_THRESHOLD 1U
+#define MAX_RTS_THRESHOLD 2304U
+#define DEFAULT_BEACON_INTERVAL 100U
+#define DEFAULT_SHORT_RETRY_LIMIT 7U
+#define DEFAULT_LONG_RETRY_LIMIT 4U
+
+/* Values are in microsecond */
+static const s32 timeout_duration[] = {
+ 350000,
+ 250000,
+ 75000,
+ 37000,
+ 25000,
+};
+
+static const s32 period_duration[] = {
+ 400000,
+ 700000,
+ 1000000,
+ 1000000,
+ 1000000
+};
+
+static int ipw_wx_get_range(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct iw_range *range = (struct iw_range *)extra;
+ u16 val;
+ int i;
+
+ wrqu->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ /* 54Mbs == ~27 Mb/s real (802.11g) */
+ range->throughput = 27 * 1000 * 1000;
+
+ range->max_qual.qual = 100;
+ /* TODO: Find real max RSSI and stick here */
+ range->max_qual.level = 0;
+ range->max_qual.noise = 0;
+ range->max_qual.updated = 7; /* Updated all three */
+
+ range->avg_qual.qual = 70;
+ /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+ range->avg_qual.level = 0; /* FIXME to real average level */
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = 7; /* Updated all three */
+
+ range->num_bitrates = min(priv->rates.num_rates, (u8)IW_MAX_BITRATES);
+
+ for (i = 0; i < range->num_bitrates; i++)
+ range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
+ 500000;
+
+ range->max_rts = DEFAULT_RTS_THRESHOLD;
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+
+ range->encoding_size[0] = 5;
+ range->encoding_size[1] = 13;
+ range->num_encoding_sizes = 2;
+ range->max_encoding_tokens = WEP_KEYS;
+
+ /* Set the Wireless Extension versions */
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 16;
+
+ range->num_channels = FREQ_COUNT;
+
+ val = 0;
+ for (i = 0; i < FREQ_COUNT; i++) {
+ range->freq[val].i = i + 1;
+ range->freq[val].m = ipw_frequencies[i] * 100000;
+ range->freq[val].e = 1;
+ val++;
+
+ if (val == IW_MAX_FREQUENCIES)
+ break;
+ }
+ range->num_frequency = val;
+
+ IPW_DEBUG_WX("GET Range\n");
+ return 0;
+}
+
+static int ipw_wx_set_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ static const unsigned char any[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+ static const unsigned char off[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
+ !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+ /* we disable mandatory BSSID association */
+ IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
+ priv->config &= ~CFG_STATIC_BSSID;
+ if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED |
+ STATUS_ASSOCIATING))) {
+ IPW_DEBUG_ASSOC("Attempting to associate with new "
+ "parameters.\n");
+ ipw_associate(priv);
+ }
+
+ return 0;
+ }
+
+ priv->config |= CFG_STATIC_BSSID;
+ if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+ IPW_DEBUG_WX("BSSID set to current BSSID.\n");
+ return 0;
+ }
+
+ IPW_DEBUG_WX("Setting mandatory BSSID to " MAC_FMT "\n",
+ MAC_ARG(wrqu->ap_addr.sa_data));
+
+ memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
+
+ /* If we are currently associated, or trying to associate
+ * then see if this is a new BSSID (causing us to disassociate) */
+ if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+ IPW_DEBUG_ASSOC("Disassociating due to BSSID change.\n");
+ ipw_disassociate(priv);
+ } else {
+ ipw_associate(priv);
+ }
+
+ return 0;
+}
+
+static int ipw_wx_get_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ /* If we are associated, trying to associate, or have a statically
+ * configured BSSID then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_BSSID ||
+ priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+ wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(wrqu->ap_addr.sa_data, &priv->bssid, ETH_ALEN);
+ } else
+ memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+
+ IPW_DEBUG_WX("Getting WAP BSSID: " MAC_FMT "\n",
+ MAC_ARG(wrqu->ap_addr.sa_data));
+ return 0;
+}
+
+static int ipw_wx_set_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ char *essid = ""; /* ANY */
+ int length = 0;
+
+ if (wrqu->essid.flags && wrqu->essid.length) {
+ length = wrqu->essid.length - 1;
+ essid = extra;
+ }
+ if (length == 0) {
+ IPW_DEBUG_WX("Setting ESSID to ANY\n");
+ priv->config &= ~CFG_STATIC_ESSID;
+ if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED |
+ STATUS_ASSOCIATING))) {
+ IPW_DEBUG_ASSOC("Attempting to associate with new "
+ "parameters.\n");
+ ipw_associate(priv);
+ }
+
+ return 0;
+ }
+
+ length = min(length, IW_ESSID_MAX_SIZE);
+
+ priv->config |= CFG_STATIC_ESSID;
+
+ if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
+ IPW_DEBUG_WX("ESSID set to current ESSID.\n");
+ return 0;
+ }
+
+ IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length),
+ length);
+
+ priv->essid_len = length;
+ memcpy(priv->essid, essid, priv->essid_len);
+
+ /* If we are currently associated, or trying to associate
+ * then see if this is a new ESSID (causing us to disassociate) */
+ if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+ IPW_DEBUG_ASSOC("Disassociating due to ESSID change.\n");
+ ipw_disassociate(priv);
+ } else {
+ ipw_associate(priv);
+ }
+
+ return 0;
+}
+
+static int ipw_wx_get_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured ESSID then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_ESSID ||
+ priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+ IPW_DEBUG_WX("Getting essid: '%s'\n",
+ escape_essid(priv->essid, priv->essid_len));
+ memcpy(extra, priv->essid, priv->essid_len);
+ wrqu->essid.length = priv->essid_len;
+ wrqu->essid.flags = 1; /* active */
+ } else {
+ IPW_DEBUG_WX("Getting essid: ANY\n");
+ wrqu->essid.length = 0;
+ wrqu->essid.flags = 0; /* active */
+ }
+
+ return 0;
+}
+
+static int ipw_wx_set_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
+ if (wrqu->data.length > IW_ESSID_MAX_SIZE)
+ return -E2BIG;
+
+ wrqu->data.length = min((size_t)wrqu->data.length, sizeof(priv->nick));
+ memset(priv->nick, 0, sizeof(priv->nick));
+ memcpy(priv->nick, extra, wrqu->data.length);
+ IPW_DEBUG_TRACE("<<\n");
+ return 0;
+
+}
+
+
+static int ipw_wx_get_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ IPW_DEBUG_WX("Getting nick\n");
+ wrqu->data.length = strlen(priv->nick) + 1;
+ memcpy(extra, priv->nick, wrqu->data.length);
+ wrqu->data.flags = 1; /* active */
+ return 0;
+}
+
+
+static int ipw_wx_set_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
+ return -EOPNOTSUPP;
+}
+
+static int ipw_wx_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv * priv = ieee80211_priv(dev);
+ wrqu->bitrate.value = priv->last_rate;
+
+ IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
+ return 0;
+}
+
+
+static int ipw_wx_set_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ if (wrqu->rts.disabled)
+ priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
+ else {
+ if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
+ wrqu->rts.value > MAX_RTS_THRESHOLD)
+ return -EINVAL;
+
+ priv->rts_threshold = wrqu->rts.value;
+ }
+
+ ipw_send_rts_threshold(priv, priv->rts_threshold);
+ IPW_DEBUG_WX("SET RTS Threshold -> %d \n", priv->rts_threshold);
+ return 0;
+}
+
+static int ipw_wx_get_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ wrqu->rts.value = priv->rts_threshold;
+ wrqu->rts.fixed = 0; /* no auto select */
+ wrqu->rts.disabled =
+ (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
+
+ IPW_DEBUG_WX("GET RTS Threshold -> %d \n", wrqu->rts.value);
+ return 0;
+}
+
+
+static int ipw_wx_set_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct ipw_tx_power tx_power;
+ int i;
+
+ if (ipw_radio_kill_sw(priv, wrqu->power.disabled))
+ return -EINPROGRESS;
+
+ if (wrqu->power.flags != IW_TXPOW_DBM)
+ return -EINVAL;
+
+ if ((wrqu->power.value > 20) ||
+ (wrqu->power.value < -12))
+ return -EINVAL;
+
+ priv->tx_power = wrqu->power.value;
+
+ memset(&tx_power, 0, sizeof(tx_power));
+
+ /* configure device for 'G' band */
+ tx_power.ieee_mode = IPW_G_MODE;
+ tx_power.num_channels = 11;
+ for (i = 0; i < 11; i++) {
+ tx_power.channels_tx_power[i].channel_number = i + 1;
+ tx_power.channels_tx_power[i].tx_power = priv->tx_power;
+ }
+ if (ipw_send_tx_power(priv, &tx_power))
+ goto error;
+
+ /* configure device to also handle 'B' band */
+ tx_power.ieee_mode = IPW_B_MODE;
+ if (ipw_send_tx_power(priv, &tx_power))
+ goto error;
+
+ return 0;
+
+ error:
+ return -EIO;
+}
+
+
+static int ipw_wx_get_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ wrqu->power.value = priv->tx_power;
+ wrqu->power.fixed = 1;
+ wrqu->power.flags = IW_TXPOW_DBM;
+ wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
+
+ IPW_DEBUG_WX("GET TX Power -> %s %d \n",
+ wrqu->power.disabled ? "ON" : "OFF",
+ wrqu->power.value);
+
+ return 0;
+}
+
+static int ipw_wx_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ if (wrqu->frag.disabled)
+ priv->ieee->fts = DEFAULT_FTS;
+ else {
+ if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
+ wrqu->frag.value > MAX_FRAG_THRESHOLD)
+ return -EINVAL;
+
+ priv->ieee->fts = wrqu->frag.value & ~0x1;
+ }
+
+ ipw_send_frag_threshold(priv, wrqu->frag.value);
+ IPW_DEBUG_WX("SET Frag Threshold -> %d \n", wrqu->frag.value);
+ return 0;
+}
+
+static int ipw_wx_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ wrqu->frag.value = priv->ieee->fts;
+ wrqu->frag.fixed = 0; /* no auto select */
+ wrqu->frag.disabled =
+ (wrqu->frag.value == DEFAULT_FTS);
+
+ IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
+
+ return 0;
+}
+
+static int ipw_wx_set_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
+ return -EOPNOTSUPP;
+}
+
+
+static int ipw_wx_get_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu);
+ return -EOPNOTSUPP;
+}
+
+
+static int ipw_wx_set_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ IPW_DEBUG_WX("Start scan\n");
+ if (ipw_request_scan(priv))
+ return -EIO;
+ return 0;
+}
+
+static int ipw_wx_get_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
+}
+
+static int ipw_wx_set_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw_wx_get_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw_wx_set_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int err;
+
+ if (wrqu->power.disabled) {
+ priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
+ err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
+ if (err) {
+ IPW_DEBUG_WX("failed setting power mode.\n");
+ return err;
+ }
+
+ IPW_DEBUG_WX("SET Power Management Mode -> off\n");
+
+ return 0;
+ }
+
+ switch (wrqu->power.flags & IW_POWER_MODE) {
+ case IW_POWER_ON: /* If not specified */
+ case IW_POWER_MODE: /* If set all mask */
+ case IW_POWER_ALL_R: /* If explicitely state all */
+ break;
+ default: /* Otherwise we don't support it */
+ IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
+ wrqu->power.flags);
+ return -EOPNOTSUPP;
+ }
+
+ /* If the user hasn't specified a power management mode yet, default
+ * to BATTERY */
+ if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
+ priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
+ else
+ priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
+ err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
+ if (err) {
+ IPW_DEBUG_WX("failed setting power mode.\n");
+ return err;
+ }
+
+ IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n",
+ priv->power_mode);
+
+ return 0;
+}
+
+static int ipw_wx_get_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ if (!(priv->power_mode & IPW_POWER_ENABLED)) {
+ wrqu->power.disabled = 1;
+ } else {
+ wrqu->power.disabled = 0;
+ }
+
+ IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
+
+ return 0;
+}
+
+static int ipw_wx_set_powermode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int mode = *(int *)extra;
+ int err;
+
+ if ((mode < 1) || (mode > IPW_POWER_LIMIT)) {
+ mode = IPW_POWER_AC;
+ priv->power_mode = mode;
+ } else {
+ priv->power_mode = IPW_POWER_ENABLED | mode;
+ }
+
+ if (priv->power_mode != mode) {
+ err = ipw_send_power_mode(priv, mode);
+
+ if (err) {
+ IPW_DEBUG_WX("failed setting power mode.\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+#define MAX_WX_STRING 80
+static int ipw_wx_get_powermode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int level = IPW_POWER_LEVEL(priv->power_mode);
+ char *p = extra;
+
+ p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
+
+ switch (level) {
+ case IPW_POWER_AC:
+ p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
+ break;
+ case IPW_POWER_BATTERY:
+ p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
+ break;
+ default:
+ p += snprintf(p, MAX_WX_STRING - (p - extra),
+ "(Timeout %dms, Period %dms)",
+ timeout_duration[level - 1] / 1000,
+ period_duration[level - 1] / 1000);
+ }
+
+ if (!(priv->power_mode & IPW_POWER_ENABLED))
+ p += snprintf(p, MAX_WX_STRING - (p - extra)," OFF");
+
+ wrqu->data.length = p - extra + 1;
+
+ return 0;
+}
+
+static int ipw_wx_set_wireless_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int mode = *(int *)extra;
+ u8 band = 0, modulation = 0;
+
+ if (mode == 0 || mode & ~IEEE_MODE_MASK) {
+ IPW_WARNING("Attempt to set invalid wireless mode: %d\n",
+ mode);
+ return -EINVAL;
+ }
+
+ if (priv->adapter == IPW_2915ABG) {
+ priv->ieee->abg_ture = 1;
+ if (mode & IEEE_A) {
+ band |= IEEE80211_52GHZ_BAND;
+ modulation |= IEEE80211_OFDM_MODULATION;
+ } else
+ priv->ieee->abg_ture = 0;
+ } else {
+ if (mode & IEEE_A) {
+ IPW_WARNING("Attempt to set 2200BG into "
+ "802.11a mode\n");
+ return -EINVAL;
+ }
+
+ priv->ieee->abg_ture = 0;
+ }
+
+ if (mode & IEEE_B) {
+ band |= IEEE80211_24GHZ_BAND;
+ modulation |= IEEE80211_CCK_MODULATION;
+ } else
+ priv->ieee->abg_ture = 0;
+
+ if (mode & IEEE_G) {
+ band |= IEEE80211_24GHZ_BAND;
+ modulation |= IEEE80211_OFDM_MODULATION;
+ } else
+ priv->ieee->abg_ture = 0;
+
+ priv->ieee->mode = mode;
+ priv->ieee->freq_band = band;
+ priv->ieee->modulation = modulation;
+ init_supported_rates(priv, &priv->rates);
+
+ /* If we are currently associated, or trying to associate
+ * then see if this is a new configuration (causing us to
+ * disassociate) */
+ if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+ /* The resulting association will trigger
+ * the new rates to be sent to the device */
+ IPW_DEBUG_ASSOC("Disassociating due to mode change.\n");
+ ipw_disassociate(priv);
+ } else
+ ipw_send_supported_rates(priv, &priv->rates);
+
+ IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
+ mode & IEEE_A ? 'a' : '.',
+ mode & IEEE_B ? 'b' : '.',
+ mode & IEEE_G ? 'g' : '.');
+ return 0;
+}
+
+static int ipw_wx_get_wireless_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ switch (priv->ieee->freq_band) {
+ case IEEE80211_24GHZ_BAND:
+ switch (priv->ieee->modulation) {
+ case IEEE80211_CCK_MODULATION:
+ strncpy(extra, "802.11b (2)", MAX_WX_STRING);
+ break;
+ case IEEE80211_OFDM_MODULATION:
+ strncpy(extra, "802.11g (4)", MAX_WX_STRING);
+ break;
+ default:
+ strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
+ break;
+ }
+ break;
+
+ case IEEE80211_52GHZ_BAND:
+ strncpy(extra, "802.11a (1)", MAX_WX_STRING);
+ break;
+
+ default: /* Mixed Band */
+ switch (priv->ieee->modulation) {
+ case IEEE80211_CCK_MODULATION:
+ strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
+ break;
+ case IEEE80211_OFDM_MODULATION:
+ strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
+ break;
+ default:
+ strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
+ break;
+ }
+ break;
+ }
+
+ IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
+
+ wrqu->data.length = strlen(extra) + 1;
+
+ return 0;
+}
+
+#ifdef CONFIG_IPW_PROMISC
+static int ipw_wx_set_promisc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int *parms = (int *)extra;
+ int enable = (parms[0] > 0);
+
+ IPW_DEBUG_WX("SET PROMISC: %d %d\n", enable, parms[1]);
+ if (enable) {
+ if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+ priv->net_dev->type = ARPHRD_IEEE80211;
+ ipw_adapter_restart(priv);
+ }
+
+ ipw_set_channel(priv, parms[1]);
+ } else {
+ if (priv->ieee->iw_mode != IW_MODE_MONITOR)
+ return 0;
+ priv->net_dev->type = ARPHRD_ETHER;
+ ipw_adapter_restart(priv);
+ }
+ return 0;
+}
+
+
+static int ipw_wx_reset(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ IPW_DEBUG_WX("RESET\n");
+ ipw_adapter_restart(priv);
+ return 0;
+}
+#endif // CONFIG_IPW_PROMISC
+
+/* Rebase the WE IOCTLs to zero for the handler array */
+#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
+static iw_handler ipw_wx_handlers[] =
+{
+ IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name,
+ IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
+ IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
+ IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
+ IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode,
+ IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range,
+ IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap,
+ IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap,
+ IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan,
+ IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan,
+ IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid,
+ IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid,
+ IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick,
+ IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick,
+ IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate,
+ IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate,
+ IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts,
+ IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts,
+ IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag,
+ IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag,
+ IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow,
+ IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow,
+ IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry,
+ IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry,
+ IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
+ IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
+ IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power,
+ IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power,
+};
+
+#define IPW_PRIV_SET_POWER SIOCIWFIRSTPRIV
+#define IPW_PRIV_GET_POWER SIOCIWFIRSTPRIV+1
+#define IPW_PRIV_SET_MODE SIOCIWFIRSTPRIV+2
+#define IPW_PRIV_GET_MODE SIOCIWFIRSTPRIV+3
+#define IPW_PRIV_SET_PROMISC SIOCIWFIRSTPRIV+4
+#define IPW_PRIV_RESET SIOCIWFIRSTPRIV+5
+
+
+static struct iw_priv_args ipw_priv_args[] = {
+ {
+ .cmd = IPW_PRIV_SET_POWER,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_power"
+ },
+ {
+ .cmd = IPW_PRIV_GET_POWER,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_power"
+ },
+ {
+ .cmd = IPW_PRIV_SET_MODE,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_mode"
+ },
+ {
+ .cmd = IPW_PRIV_GET_MODE,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_mode"
+ },
+#ifdef CONFIG_IPW_PROMISC
+ {
+ IPW_PRIV_SET_PROMISC,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"
+ },
+ {
+ IPW_PRIV_RESET,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"
+ },
+#endif /* CONFIG_IPW_PROMISC */
+};
+
+static iw_handler ipw_priv_handler[] = {
+ ipw_wx_set_powermode,
+ ipw_wx_get_powermode,
+ ipw_wx_set_wireless_mode,
+ ipw_wx_get_wireless_mode,
+#ifdef CONFIG_IPW_PROMISC
+ ipw_wx_set_promisc,
+ ipw_wx_reset,
+#endif
+};
+
+static struct iw_handler_def ipw_wx_handler_def =
+{
+ .standard = ipw_wx_handlers,
+ .num_standard = ARRAY_SIZE(ipw_wx_handlers),
+ .num_private = ARRAY_SIZE(ipw_priv_handler),
+ .num_private_args = ARRAY_SIZE(ipw_priv_args),
+ .private = ipw_priv_handler,
+ .private_args = ipw_priv_args,
+};
+
+
+
+
+/*
+ * Get wireless statistics.
+ * Called by /proc/net/wireless
+ * Also called by SIOCGIWSTATS
+ */
+static struct iw_statistics *ipw_get_wireless_stats(struct net_device * dev)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct iw_statistics *wstats;
+
+ wstats = &priv->wstats;
+
+ /* if hw is disabled, then ipw2100_get_ordinal() can't be called.
+ * ipw2100_wx_wireless_stats seems to be called before fw is
+ * initialized. STATUS_ASSOCIATED will only be set if the hw is up
+ * and associated; if not associcated, the values are all meaningless
+ * anyway, so set them all to NULL and INVALID */
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ wstats->miss.beacon = 0;
+ wstats->discard.retries = 0;
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ wstats->qual.noise = 0;
+ wstats->qual.updated = 7;
+ wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
+ IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+ return wstats;
+ }
+
+ wstats->qual.qual = priv->quality;
+ wstats->qual.level = average_value(&priv->average_rssi);
+ wstats->qual.noise = average_value(&priv->average_noise);
+ wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
+ IW_QUAL_NOISE_UPDATED;
+
+ wstats->miss.beacon = average_value(&priv->average_missed_beacons);
+ wstats->discard.retries = priv->last_tx_failures;
+ wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
+
+/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
+ goto fail_get_ordinal;
+ wstats->discard.retries += tx_retry; */
+
+ return wstats;
+}
+
+
+/* net device stuff */
+
+static inline void init_sys_config(struct ipw_sys_config *sys_config)
+{
+ memset(sys_config, 0, sizeof(struct ipw_sys_config));
+ sys_config->bt_coexistence = 1; /* We may need to look into prvStaBtConfig */
+ sys_config->answer_broadcast_ssid_probe = 0;
+ sys_config->accept_all_data_frames = 0;
+ sys_config->accept_non_directed_frames = 1;
+ sys_config->exclude_unicast_unencrypted = 0;
+ sys_config->disable_unicast_decryption = 1;
+ sys_config->exclude_multicast_unencrypted = 0;
+ sys_config->disable_multicast_decryption = 1;
+ sys_config->antenna_diversity = CFG_SYS_ANTENNA_BOTH;
+ sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
+ sys_config->dot11g_auto_detection = 0;
+ sys_config->enable_cts_to_self = 0;
+ sys_config->bt_coexist_collision_thr = 0;
+ sys_config->pass_noise_stats_to_host = 1;
+}
+
+static int ipw_net_open(struct net_device *dev)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ IPW_DEBUG_INFO("dev->open\n");
+ /* we should be verifying the device is ready to be opened */
+ if (!(priv->status & STATUS_RF_KILL_MASK) &&
+ (priv->status & STATUS_ASSOCIATED))
+ netif_start_queue(dev);
+ return 0;
+}
+
+static int ipw_net_stop(struct net_device *dev)
+{
+ IPW_DEBUG_INFO("dev->close\n");
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/*
+todo:
+
+modify to send one tfd per fragment instead of using chunking. otherwise
+we need to heavily modify the ieee80211_skb_to_txb.
+*/
+
+static inline void ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)
+ txb->fragments[0]->data;
+ int i = 0;
+ struct tfd_frame *tfd;
+ struct clx2_tx_queue *txq = &priv->txq[0];
+ struct clx2_queue *q = &txq->q;
+ u8 id, hdr_len, unicast;
+ u16 remaining_bytes;
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ hdr_len = IEEE80211_3ADDR_LEN;
+ unicast = !is_broadcast_ether_addr(hdr->addr1) &&
+ !is_multicast_ether_addr(hdr->addr1);
+ id = ipw_find_station(priv, hdr->addr1);
+ if (id == IPW_INVALID_STATION) {
+ id = ipw_add_station(priv, hdr->addr1);
+ if (id == IPW_INVALID_STATION) {
+ IPW_WARNING("Attempt to send data to "
+ "invalid cell: " MAC_FMT "\n",
+ MAC_ARG(hdr->addr1));
+ goto drop;
+ }
+ }
+ break;
+
+ case IW_MODE_INFRA:
+ default:
+ unicast = !is_broadcast_ether_addr(hdr->addr3) &&
+ !is_multicast_ether_addr(hdr->addr3);
+ hdr_len = IEEE80211_3ADDR_LEN;
+ id = 0;
+ break;
+ }
+
+ tfd = &txq->bd[q->first_empty];
+ txq->txb[q->first_empty] = txb;
+ memset(tfd, 0, sizeof(*tfd));
+ tfd->u.data.station_number = id;
+
+ tfd->control_flags.message_type = TX_FRAME_TYPE;
+ tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
+
+ tfd->u.data.cmd_id = DINO_CMD_TX;
+ tfd->u.data.len = txb->payload_size;
+ remaining_bytes = txb->payload_size;
+ if (unlikely(!unicast))
+ tfd->u.data.tx_flags = DCT_FLAG_NO_WEP;
+ else
+ tfd->u.data.tx_flags = DCT_FLAG_NO_WEP | DCT_FLAG_ACK_REQD;
+
+ if (priv->assoc_request.ieee_mode == IPW_B_MODE)
+ tfd->u.data.tx_flags_ext = DCT_FLAG_EXT_MODE_CCK;
+ else
+ tfd->u.data.tx_flags_ext = DCT_FLAG_EXT_MODE_OFDM;
+
+ if (priv->config & CFG_PREAMBLE)
+ tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREMBL;
+
+ memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
+
+ /* payload */
+ tfd->u.data.num_chunks = min((u8)(NUM_TFD_CHUNKS - 2), txb->nr_frags);
+ for (i = 0; i < tfd->u.data.num_chunks; i++) {
+ IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
+ i, tfd->u.data.num_chunks,
+ txb->fragments[i]->len - hdr_len);
+ printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
+ txb->fragments[i]->len - hdr_len);
+
+ tfd->u.data.chunk_ptr[i] = pci_map_single(
+ priv->pci_dev, txb->fragments[i]->data + hdr_len,
+ txb->fragments[i]->len - hdr_len, PCI_DMA_TODEVICE);
+ tfd->u.data.chunk_len[i] = txb->fragments[i]->len - hdr_len;
+ }
+
+ if (i != txb->nr_frags) {
+ struct sk_buff *skb;
+ u16 remaining_bytes = 0;
+ int j;
+
+ for (j = i; j < txb->nr_frags; j++)
+ remaining_bytes += txb->fragments[j]->len - hdr_len;
+
+ printk(KERN_INFO "Trying to reallocate for %d bytes\n",
+ remaining_bytes);
+ skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
+ if (skb != NULL) {
+ tfd->u.data.chunk_len[i] = remaining_bytes;
+ for (j = i; j < txb->nr_frags; j++) {
+ int size = txb->fragments[j]->len - hdr_len;
+ printk(KERN_INFO "Adding frag %d %d...\n",
+ j, size);
+ memcpy(skb_put(skb, size),
+ txb->fragments[j]->data + hdr_len,
+ size);
+ }
+ dev_kfree_skb_any(txb->fragments[i]);
+ txb->fragments[i] = skb;
+ tfd->u.data.chunk_ptr[i] = pci_map_single(
+ priv->pci_dev, skb->data,
+ tfd->u.data.chunk_len[i], PCI_DMA_TODEVICE);
+ tfd->u.data.num_chunks++;
+ }
+ }
+
+ /* kick DMA */
+ q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
+ ipw_write32(priv, q->reg_w, q->first_empty);
+
+ if (ipw_queue_space(q) < q->high_mark)
+ netif_stop_queue(priv->net_dev);
+
+ return;
+
+ drop:
+ IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
+ ieee80211_txb_free(txb);
+}
+
+static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
+ struct net_device *dev)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ unsigned long flags;
+
+ IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ IPW_DEBUG_INFO("Tx attempt while not associated.\n");
+ priv->ieee->stats.tx_carrier_errors++;
+ netif_stop_queue(dev);
+ goto fail_unlock;
+ }
+
+ ipw_tx_skb(priv, txb);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return 0;
+
+ fail_unlock:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return 1;
+}
+
+static struct net_device_stats *ipw_net_get_stats(struct net_device *dev)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ priv->ieee->stats.tx_packets = priv->tx_packets;
+ priv->ieee->stats.rx_packets = priv->rx_packets;
+ return &priv->ieee->stats;
+}
+
+static void ipw_net_set_multicast_list(struct net_device *dev)
+{
+
+}
+
+static int ipw_net_set_mac_address(struct net_device *dev, void *p)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct sockaddr *addr = p;
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+ priv->config |= CFG_CUSTOM_MAC;
+ memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
+ printk(KERN_INFO "%s: Setting MAC to " MAC_FMT "\n",
+ priv->net_dev->name, MAC_ARG(priv->mac_addr));
+ ipw_adapter_restart(priv);
+ return 0;
+}
+
+static void ipw_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct ipw_priv *p = ieee80211_priv(dev);
+ char vers[64];
+ char date[32];
+ u32 len;
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+
+ len = sizeof(vers);
+ ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
+ len = sizeof(date);
+ ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
+
+ snprintf(info->fw_version, sizeof(info->fw_version),"%s (%s)",
+ vers, date);
+ strcpy(info->bus_info, pci_name(p->pci_dev));
+ info->eedump_len = CX2_EEPROM_IMAGE_SIZE;
+}
+
+static u32 ipw_ethtool_get_link(struct net_device *dev)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ return (priv->status & STATUS_ASSOCIATED) != 0;
+}
+
+static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
+{
+ return CX2_EEPROM_IMAGE_SIZE;
+}
+
+static int ipw_ethtool_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+ struct ipw_priv *p = ieee80211_priv(dev);
+
+ if (eeprom->offset + eeprom->len > CX2_EEPROM_IMAGE_SIZE)
+ return -EINVAL;
+
+ memcpy(bytes, &((u8 *)p->eeprom)[eeprom->offset], eeprom->len);
+ return 0;
+}
+
+static int ipw_ethtool_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+ struct ipw_priv *p = ieee80211_priv(dev);
+ int i;
+
+ if (eeprom->offset + eeprom->len > CX2_EEPROM_IMAGE_SIZE)
+ return -EINVAL;
+
+ memcpy(&((u8 *)p->eeprom)[eeprom->offset], bytes, eeprom->len);
+ for (i = IPW_EEPROM_DATA;
+ i < IPW_EEPROM_DATA + CX2_EEPROM_IMAGE_SIZE;
+ i++)
+ ipw_write8(p, i, p->eeprom[i]);
+
+ return 0;
+}
+
+static struct ethtool_ops ipw_ethtool_ops = {
+ .get_link = ipw_ethtool_get_link,
+ .get_drvinfo = ipw_ethtool_get_drvinfo,
+ .get_eeprom_len = ipw_ethtool_get_eeprom_len,
+ .get_eeprom = ipw_ethtool_get_eeprom,
+ .set_eeprom = ipw_ethtool_set_eeprom,
+};
+
+static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
+{
+ struct ipw_priv *priv = data;
+ u32 inta, inta_mask;
+
+ if (!priv)
+ return IRQ_NONE;
+
+ spin_lock(&priv->lock);
+
+ if (!(priv->status & STATUS_INT_ENABLED)) {
+ /* Shared IRQ */
+ goto none;
+ }
+
+ inta = ipw_read32(priv, CX2_INTA_RW);
+ inta_mask = ipw_read32(priv, CX2_INTA_MASK_R);
+
+ if (inta == 0xFFFFFFFF) {
+ /* Hardware disappeared */
+ IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
+ goto none;
+ }
+
+ if (!(inta & (CX2_INTA_MASK_ALL & inta_mask))) {
+ /* Shared interrupt */
+ goto none;
+ }
+
+ /* tell the device to stop sending interrupts */
+ ipw_disable_interrupts(priv);
+
+ /* ack current interrupts */
+ inta &= (CX2_INTA_MASK_ALL & inta_mask);
+ ipw_write32(priv, CX2_INTA_RW, inta);
+
+ /* Cache INTA value for our tasklet */
+ priv->isr_inta = inta;
+
+ tasklet_schedule(&priv->irq_tasklet);
+
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+ none:
+ spin_unlock(&priv->lock);
+ return IRQ_NONE;
+}
+
+static void ipw_rf_kill(void *adapter)
+{
+ struct ipw_priv *priv = adapter;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
+ if (priv->workqueue)
+ queue_delayed_work(priv->workqueue,
+ &priv->rf_kill, 2 * HZ);
+ goto exit_unlock;
+ }
+
+ /* RF Kill is now disabled, so bring the device back up */
+
+ if (!(priv->status & STATUS_RF_KILL_MASK)) {
+ IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
+ "device\n");
+
+ /* we can not do an adapter restart while inside an irq lock */
+ queue_work(priv->workqueue, &priv->adapter_restart);
+ } else
+ IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
+ "enabled\n");
+
+ exit_unlock:
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int ipw_setup_deferred_work(struct ipw_priv *priv)
+{
+ int ret = 0;
+
+ priv->workqueue = create_workqueue(DRV_NAME);
+ init_waitqueue_head(&priv->wait_command_queue);
+
+ INIT_WORK(&priv->adhoc_check, ipw_adhoc_check, priv);
+ INIT_WORK(&priv->associate, ipw_associate, priv);
+ INIT_WORK(&priv->disassociate, ipw_disassociate, priv);
+ INIT_WORK(&priv->rx_replenish, ipw_rx_queue_replenish, priv);
+ INIT_WORK(&priv->adapter_restart, ipw_adapter_restart, priv);
+ INIT_WORK(&priv->rf_kill, ipw_rf_kill, priv);
+ INIT_WORK(&priv->up, (void (*)(void *))ipw_up, priv);
+ INIT_WORK(&priv->down, (void (*)(void *))ipw_down, priv);
+ INIT_WORK(&priv->request_scan,
+ (void (*)(void *))ipw_request_scan, priv);
+ INIT_WORK(&priv->gather_stats,
+ (void (*)(void *))ipw_gather_stats, priv);
+ INIT_WORK(&priv->abort_scan, (void (*)(void *))ipw_abort_scan, priv);
+ INIT_WORK(&priv->roam, ipw_roam, priv);
+ INIT_WORK(&priv->scan_check, ipw_scan_check, priv);
+
+ tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+ ipw_irq_tasklet, (unsigned long)priv);
+
+ return ret;
+}
+
+
+static void shim__set_security(struct net_device *dev,
+ struct ieee80211_security *sec)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (sec->flags & (1 << i)) {
+ priv->sec.key_sizes[i] = sec->key_sizes[i];
+ if (sec->key_sizes[i] == 0)
+ priv->sec.flags &= ~(1 << i);
+ else
+ memcpy(priv->sec.keys[i], sec->keys[i],
+ sec->key_sizes[i]);
+ priv->sec.flags |= (1 << i);
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+ }
+
+ if ((sec->flags & SEC_ACTIVE_KEY) &&
+ priv->sec.active_key != sec->active_key) {
+ if (sec->active_key <= 3) {
+ priv->sec.active_key = sec->active_key;
+ priv->sec.flags |= SEC_ACTIVE_KEY;
+ } else
+ priv->sec.flags &= ~SEC_ACTIVE_KEY;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ if ((sec->flags & SEC_AUTH_MODE) &&
+ (priv->sec.auth_mode != sec->auth_mode)) {
+ priv->sec.auth_mode = sec->auth_mode;
+ priv->sec.flags |= SEC_AUTH_MODE;
+ if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
+ priv->capability |= CAP_SHARED_KEY;
+ else
+ priv->capability &= ~CAP_SHARED_KEY;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ if (sec->flags & SEC_ENABLED &&
+ priv->sec.enabled != sec->enabled) {
+ priv->sec.flags |= SEC_ENABLED;
+ priv->sec.enabled = sec->enabled;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ if (sec->enabled)
+ priv->capability |= CAP_PRIVACY_ON;
+ else
+ priv->capability &= ~CAP_PRIVACY_ON;
+ }
+
+ if (sec->flags & SEC_LEVEL &&
+ priv->sec.level != sec->level) {
+ priv->sec.level = sec->level;
+ priv->sec.flags |= SEC_LEVEL;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ /* To match current functionality of ipw2100 (which works well w/
+ * various supplicants, we don't force a disassociate if the
+ * privacy capability changes ... */
+#if 0
+ if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
+ (((priv->assoc_request.capability &
+ WLAN_CAPABILITY_PRIVACY) && !sec->enabled) ||
+ (!(priv->assoc_request.capability &
+ WLAN_CAPABILITY_PRIVACY) && sec->enabled))) {
+ IPW_DEBUG_ASSOC("Disassociating due to capability "
+ "change.\n");
+ ipw_disassociate(priv);
+ }
+#endif
+}
+
+static int init_supported_rates(struct ipw_priv *priv,
+ struct ipw_supported_rates *rates)
+{
+ /* TODO: Mask out rates based on priv->rates_mask */
+
+ memset(rates, 0, sizeof(*rates));
+ /* configure supported rates */
+ switch (priv->ieee->freq_band) {
+ case IEEE80211_52GHZ_BAND:
+ rates->ieee_mode = IPW_A_MODE;
+ rates->purpose = IPW_RATE_CAPABILITIES;
+ ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
+ IEEE80211_OFDM_DEFAULT_RATES_MASK);
+ break;
+
+ default: /* Mixed or 2.4Ghz */
+ rates->ieee_mode = IPW_G_MODE;
+ rates->purpose = IPW_RATE_CAPABILITIES;
+ ipw_add_cck_scan_rates(rates, IEEE80211_CCK_MODULATION,
+ IEEE80211_CCK_DEFAULT_RATES_MASK);
+ if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) {
+ ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
+ IEEE80211_OFDM_DEFAULT_RATES_MASK);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int ipw_config(struct ipw_priv *priv)
+{
+ int i;
+ struct ipw_tx_power tx_power;
+
+ memset(&priv->sys_config, 0, sizeof(priv->sys_config));
+ memset(&tx_power, 0, sizeof(tx_power));
+
+ /* This is only called from ipw_up, which resets/reloads the firmware
+ so, we don't need to first disable the card before we configure
+ it */
+
+ /* configure device for 'G' band */
+ tx_power.ieee_mode = IPW_G_MODE;
+ tx_power.num_channels = 11;
+ for (i = 0; i < 11; i++) {
+ tx_power.channels_tx_power[i].channel_number = i + 1;
+ tx_power.channels_tx_power[i].tx_power = priv->tx_power;
+ }
+ if (ipw_send_tx_power(priv, &tx_power))
+ goto error;
+
+ /* configure device to also handle 'B' band */
+ tx_power.ieee_mode = IPW_B_MODE;
+ if (ipw_send_tx_power(priv, &tx_power))
+ goto error;
+
+ /* initialize adapter address */
+ if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
+ goto error;
+
+ /* set basic system config settings */
+ init_sys_config(&priv->sys_config);
+ if (ipw_send_system_config(priv, &priv->sys_config))
+ goto error;
+
+ init_supported_rates(priv, &priv->rates);
+ if (ipw_send_supported_rates(priv, &priv->rates))
+ goto error;
+
+ /* Set request-to-send threshold */
+ if (priv->rts_threshold) {
+ if (ipw_send_rts_threshold(priv, priv->rts_threshold))
+ goto error;
+ }
+
+ if (ipw_set_random_seed(priv))
+ goto error;
+
+ /* final state transition to the RUN state */
+ if (ipw_send_host_complete(priv))
+ goto error;
+
+ /* If configured to try and auto-associate, kick off a scan */
+ if ((priv->config & CFG_ASSOCIATE) && ipw_request_scan(priv))
+ goto error;
+
+ return 0;
+
+ error:
+ return -EIO;
+}
+
+#define MAX_HW_RESTARTS 5
+static int ipw_up(struct ipw_priv *priv)
+{
+ int rc, i;
+
+ if (priv->status & STATUS_EXIT_PENDING)
+ return -EIO;
+
+ for (i = 0; i < MAX_HW_RESTARTS; i++ ) {
+ /* Load the microcode, firmware, and eeprom.
+ * Also start the clocks. */
+ rc = ipw_load(priv);
+ if (rc) {
+ IPW_ERROR("Unable to load firmware: 0x%08X\n",
+ rc);
+ return rc;
+ }
+
+ ipw_init_ordinals(priv);
+ if (!(priv->config & CFG_CUSTOM_MAC))
+ eeprom_parse_mac(priv, priv->mac_addr);
+ memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
+
+ if (priv->status & STATUS_RF_KILL_MASK)
+ return 0;
+
+ rc = ipw_config(priv);
+ if (!rc) {
+ IPW_DEBUG_INFO("Configured device on count %i\n", i);
+ priv->notif_missed_beacons = 0;
+ netif_start_queue(priv->net_dev);
+ return 0;
+ } else {
+ IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n",
+ rc);
+ }
+
+ IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
+ i, MAX_HW_RESTARTS);
+
+ /* We had an error bringing up the hardware, so take it
+ * all the way back down so we can try again */
+ ipw_down(priv);
+ }
+
+ /* tried to restart and config the device for as long as our
+ * patience could withstand */
+ IPW_ERROR("Unable to initialize device after %d attempts.\n",
+ i);
+ return -EIO;
+}
+
+static void ipw_down(struct ipw_priv *priv)
+{
+ /* Attempt to disable the card */
+#if 0
+ ipw_send_card_disable(priv, 0);
+#endif
+
+ /* tell the device to stop sending interrupts */
+ ipw_disable_interrupts(priv);
+
+ /* Clear all bits but the RF Kill */
+ priv->status &= STATUS_RF_KILL_MASK;
+
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+
+ ipw_stop_nic(priv);
+}
+
+/* Called by register_netdev() */
+static int ipw_net_init(struct net_device *dev)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ if (priv->status & STATUS_RF_KILL_SW) {
+ IPW_WARNING("Radio disabled by module parameter.\n");
+ return 0;
+ } else if (rf_kill_active(priv)) {
+ IPW_WARNING("Radio Frequency Kill Switch is On:\n"
+ "Kill switch must be turned off for "
+ "wireless networking to work.\n");
+ queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
+ return 0;
+ }
+
+ if (ipw_up(priv))
+ return -EIO;
+
+ return 0;
+}
+
+/* PCI driver stuff */
+static struct pci_device_id card_ids[] = {
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x104f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x4220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
+ {PCI_VENDOR_ID_INTEL, 0x4221, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 2225BG */
+ {PCI_VENDOR_ID_INTEL, 0x4223, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
+ {PCI_VENDOR_ID_INTEL, 0x4224, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
+
+ /* required last entry */
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, card_ids);
+
+static struct attribute *ipw_sysfs_entries[] = {
+ &dev_attr_rf_kill.attr,
+ &dev_attr_direct_dword.attr,
+ &dev_attr_indirect_byte.attr,
+ &dev_attr_indirect_dword.attr,
+ &dev_attr_mem_gpio_reg.attr,
+ &dev_attr_command_event_reg.attr,
+ &dev_attr_nic_type.attr,
+ &dev_attr_status.attr,
+ &dev_attr_cfg.attr,
+ &dev_attr_dump_errors.attr,
+ &dev_attr_dump_events.attr,
+ &dev_attr_eeprom_delay.attr,
+ &dev_attr_ucode_version.attr,
+ &dev_attr_rtc.attr,
+ NULL
+};
+
+static struct attribute_group ipw_attribute_group = {
+ .name = NULL, /* put in device directory */
+ .attrs = ipw_sysfs_entries,
+};
+
+static int ipw_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err = 0;
+ struct net_device *net_dev;
+ void __iomem *base;
+ u32 length, val;
+ struct ipw_priv *priv;
+ int band, modulation;
+
+ net_dev = alloc_ieee80211(sizeof(struct ipw_priv));
+ if (net_dev == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ priv = ieee80211_priv(net_dev);
+ priv->ieee = netdev_priv(net_dev);
+ priv->net_dev = net_dev;
+ priv->pci_dev = pdev;
+#ifdef CONFIG_IPW_DEBUG
+ ipw_debug_level = debug;
+#endif
+ spin_lock_init(&priv->lock);
+
+ if (pci_enable_device(pdev)) {
+ err = -ENODEV;
+ goto out_free_ieee80211;
+ }
+
+ pci_set_master(pdev);
+
+ err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (!err)
+ err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
+ goto out_pci_disable_device;
+ }
+
+ pci_set_drvdata(pdev, priv);
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto out_pci_disable_device;
+
+ /* We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state */
+ pci_read_config_dword(pdev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+ length = pci_resource_len(pdev, 0);
+ priv->hw_len = length;
+
+ base = ioremap_nocache(pci_resource_start(pdev, 0), length);
+ if (!base) {
+ err = -ENODEV;
+ goto out_pci_release_regions;
+ }
+
+ priv->hw_base = base;
+ IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
+ IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
+
+ err = ipw_setup_deferred_work(priv);
+ if (err) {
+ IPW_ERROR("Unable to setup deferred work\n");
+ goto out_iounmap;
+ }
+
+ /* Initialize module parameter values here */
+ if (ifname)
+ strncpy(net_dev->name, ifname, IFNAMSIZ);
+
+ if (associate)
+ priv->config |= CFG_ASSOCIATE;
+ else
+ IPW_DEBUG_INFO("Auto associate disabled.\n");
+
+ if (auto_create)
+ priv->config |= CFG_ADHOC_CREATE;
+ else
+ IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
+
+ if (disable) {
+ priv->status |= STATUS_RF_KILL_SW;
+ IPW_DEBUG_INFO("Radio disabled.\n");
+ }
+
+ if (channel != 0) {
+ priv->config |= CFG_STATIC_CHANNEL;
+ priv->channel = channel;
+ IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
+ IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
+ /* TODO: Validate that provided channel is in range */
+ }
+
+ switch (mode) {
+ case 1:
+ priv->ieee->iw_mode = IW_MODE_ADHOC;
+ break;
+#ifdef CONFIG_IPW_PROMISC
+ case 2:
+ priv->ieee->iw_mode = IW_MODE_MONITOR;
+ break;
+#endif
+ default:
+ case 0:
+ priv->ieee->iw_mode = IW_MODE_INFRA;
+ break;
+ }
+
+ if ((priv->pci_dev->device == 0x4223) ||
+ (priv->pci_dev->device == 0x4224)) {
+ printk(KERN_INFO DRV_NAME
+ ": Detected Intel PRO/Wireless 2915ABG Network "
+ "Connection\n");
+ priv->ieee->abg_ture = 1;
+ band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND;
+ modulation = IEEE80211_OFDM_MODULATION |
+ IEEE80211_CCK_MODULATION;
+ priv->adapter = IPW_2915ABG;
+ priv->ieee->mode = IEEE_A|IEEE_G|IEEE_B;
+ } else {
+ if (priv->pci_dev->device == 0x4221)
+ printk(KERN_INFO DRV_NAME
+ ": Detected Intel PRO/Wireless 2225BG Network "
+ "Connection\n");
+ else
+ printk(KERN_INFO DRV_NAME
+ ": Detected Intel PRO/Wireless 2200BG Network "
+ "Connection\n");
+
+ priv->ieee->abg_ture = 0;
+ band = IEEE80211_24GHZ_BAND;
+ modulation = IEEE80211_OFDM_MODULATION |
+ IEEE80211_CCK_MODULATION;
+ priv->adapter = IPW_2200BG;
+ priv->ieee->mode = IEEE_G|IEEE_B;
+ }
+
+ priv->ieee->freq_band = band;
+ priv->ieee->modulation = modulation;
+
+ priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK;
+
+ priv->missed_beacon_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
+ priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
+
+ priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
+
+ /* If power management is turned on, default to AC mode */
+ priv->power_mode = IPW_POWER_AC;
+ priv->tx_power = IPW_DEFAULT_TX_POWER;
+
+ err = request_irq(pdev->irq, ipw_isr, SA_SHIRQ, DRV_NAME,
+ priv);
+ if (err) {
+ IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
+ goto out_destroy_workqueue;
+ }
+
+ SET_MODULE_OWNER(net_dev);
+ SET_NETDEV_DEV(net_dev, &pdev->dev);
+
+ priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
+ priv->ieee->set_security = shim__set_security;
+
+ net_dev->open = ipw_net_open;
+ net_dev->stop = ipw_net_stop;
+ net_dev->init = ipw_net_init;
+ net_dev->get_stats = ipw_net_get_stats;
+ net_dev->set_multicast_list = ipw_net_set_multicast_list;
+ net_dev->set_mac_address = ipw_net_set_mac_address;
+ net_dev->get_wireless_stats = ipw_get_wireless_stats;
+ net_dev->wireless_handlers = &ipw_wx_handler_def;
+ net_dev->ethtool_ops = &ipw_ethtool_ops;
+ net_dev->irq = pdev->irq;
+ net_dev->base_addr = (unsigned long )priv->hw_base;
+ net_dev->mem_start = pci_resource_start(pdev, 0);
+ net_dev->mem_end = net_dev->mem_start + pci_resource_len(pdev, 0) - 1;
+
+ err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
+ if (err) {
+ IPW_ERROR("failed to create sysfs device attributes\n");
+ goto out_release_irq;
+ }
+
+ err = register_netdev(net_dev);
+ if (err) {
+ IPW_ERROR("failed to register network device\n");
+ goto out_remove_group;
+ }
+
+ return 0;
+
+ out_remove_group:
+ sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
+ out_release_irq:
+ free_irq(pdev->irq, priv);
+ out_destroy_workqueue:
+ destroy_workqueue(priv->workqueue);
+ priv->workqueue = NULL;
+ out_iounmap:
+ iounmap(priv->hw_base);
+ out_pci_release_regions:
+ pci_release_regions(pdev);
+ out_pci_disable_device:
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ out_free_ieee80211:
+ free_ieee80211(priv->net_dev);
+ out:
+ return err;
+}
+
+static void ipw_pci_remove(struct pci_dev *pdev)
+{
+ struct ipw_priv *priv = pci_get_drvdata(pdev);
+ if (!priv)
+ return;
+
+ priv->status |= STATUS_EXIT_PENDING;
+
+ sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
+
+ ipw_down(priv);
+
+ unregister_netdev(priv->net_dev);
+
+ if (priv->rxq) {
+ ipw_rx_queue_free(priv, priv->rxq);
+ priv->rxq = NULL;
+ }
+ ipw_tx_queue_free(priv);
+
+ /* ipw_down will ensure that there is no more pending work
+ * in the workqueue's, so we can safely remove them now. */
+ if (priv->workqueue) {
+ cancel_delayed_work(&priv->adhoc_check);
+ cancel_delayed_work(&priv->gather_stats);
+ cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->rf_kill);
+ cancel_delayed_work(&priv->scan_check);
+ destroy_workqueue(priv->workqueue);
+ priv->workqueue = NULL;
+ }
+
+ free_irq(pdev->irq, priv);
+ iounmap(priv->hw_base);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ free_ieee80211(priv->net_dev);
+
+#ifdef CONFIG_PM
+ if (fw_loaded) {
+ release_firmware(bootfw);
+ release_firmware(ucode);
+ release_firmware(firmware);
+ fw_loaded = 0;
+ }
+#endif
+}
+
+
+#ifdef CONFIG_PM
+static int ipw_pci_suspend(struct pci_dev *pdev, u32 state)
+{
+ struct ipw_priv *priv = pci_get_drvdata(pdev);
+ struct net_device *dev = priv->net_dev;
+
+ printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
+
+ /* Take down the device; powers it off, etc. */
+ ipw_down(priv);
+
+ /* Remove the PRESENT state of the device */
+ netif_device_detach(dev);
+
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, state);
+
+ return 0;
+}
+
+static int ipw_pci_resume(struct pci_dev *pdev)
+{
+ struct ipw_priv *priv = pci_get_drvdata(pdev);
+ struct net_device *dev = priv->net_dev;
+ u32 val;
+
+ printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
+
+ pci_set_power_state(pdev, 0);
+ pci_enable_device(pdev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
+ pci_restore_state(pdev, priv->pm_state);
+#else
+ pci_restore_state(pdev);
+#endif
+ /*
+ * Suspend/Resume resets the PCI configuration space, so we have to
+ * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
+ * from interfering with C3 CPU state. pci_restore_state won't help
+ * here since it only restores the first 64 bytes pci config header.
+ */
+ pci_read_config_dword(pdev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+ /* Set the device back into the PRESENT state; this will also wake
+ * the queue of needed */
+ netif_device_attach(dev);
+
+ /* Bring the device back up */
+ queue_work(priv->workqueue, &priv->up);
+
+ return 0;
+}
+#endif
+
+/* driver initialization stuff */
+static struct pci_driver ipw_driver = {
+ .name = DRV_NAME,
+ .id_table = card_ids,
+ .probe = ipw_pci_probe,
+ .remove = __devexit_p(ipw_pci_remove),
+#ifdef CONFIG_PM
+ .suspend = ipw_pci_suspend,
+ .resume = ipw_pci_resume,
+#endif
+};
+
+static int __init ipw_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
+ printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
+
+ ret = pci_module_init(&ipw_driver);
+ if (ret) {
+ IPW_ERROR("Unable to initialize PCI module\n");
+ return ret;
+ }
+
+ ret = driver_create_file(&ipw_driver.driver,
+ &driver_attr_debug_level);
+ if (ret) {
+ IPW_ERROR("Unable to create driver sysfs file\n");
+ pci_unregister_driver(&ipw_driver);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void __exit ipw_exit(void)
+{
+ driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
+ pci_unregister_driver(&ipw_driver);
+}
+
+module_param(disable, int, 0444);
+MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
+
+module_param(associate, int, 0444);
+MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
+
+module_param(auto_create, int, 0444);
+MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
+
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "debug output mask");
+
+module_param(channel, int, 0444);
+MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
+
+module_param(ifname, charp, 0444);
+MODULE_PARM_DESC(ifname, "network device name (default eth%d)");
+
+#ifdef CONFIG_IPW_PROMISC
+module_param(mode, int, 0444);
+MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
+#else
+module_param(mode, int, 0444);
+MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
+#endif
+
+module_exit(ipw_exit);
+module_init(ipw_init);
diff --git a/drivers/net/wireless/ipw2200.h b/drivers/net/wireless/ipw2200.h
new file mode 100644
index 000000000000..3bff09d93154
--- /dev/null
+++ b/drivers/net/wireless/ipw2200.h
@@ -0,0 +1,1742 @@
+/******************************************************************************
+
+ Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************/
+
+#ifndef __ipw2200_h__
+#define __ipw2200_h__
+
+#define WEXT_USECHANNELS 1
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <linux/version.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+
+#include <linux/firmware.h>
+#include <linux/wireless.h>
+#include <asm/io.h>
+
+#include <net/ieee80211.h>
+
+#define DRV_NAME "ipw2200"
+
+#include <linux/workqueue.h>
+
+/* Authentication and Association States */
+enum connection_manager_assoc_states
+{
+ CMAS_INIT = 0,
+ CMAS_TX_AUTH_SEQ_1,
+ CMAS_RX_AUTH_SEQ_2,
+ CMAS_AUTH_SEQ_1_PASS,
+ CMAS_AUTH_SEQ_1_FAIL,
+ CMAS_TX_AUTH_SEQ_3,
+ CMAS_RX_AUTH_SEQ_4,
+ CMAS_AUTH_SEQ_2_PASS,
+ CMAS_AUTH_SEQ_2_FAIL,
+ CMAS_AUTHENTICATED,
+ CMAS_TX_ASSOC,
+ CMAS_RX_ASSOC_RESP,
+ CMAS_ASSOCIATED,
+ CMAS_LAST
+};
+
+
+#define IPW_WAIT (1<<0)
+#define IPW_QUIET (1<<1)
+#define IPW_ROAMING (1<<2)
+
+#define IPW_POWER_MODE_CAM 0x00 //(always on)
+#define IPW_POWER_INDEX_1 0x01
+#define IPW_POWER_INDEX_2 0x02
+#define IPW_POWER_INDEX_3 0x03
+#define IPW_POWER_INDEX_4 0x04
+#define IPW_POWER_INDEX_5 0x05
+#define IPW_POWER_AC 0x06
+#define IPW_POWER_BATTERY 0x07
+#define IPW_POWER_LIMIT 0x07
+#define IPW_POWER_MASK 0x0F
+#define IPW_POWER_ENABLED 0x10
+#define IPW_POWER_LEVEL(x) ((x) & IPW_POWER_MASK)
+
+#define IPW_CMD_HOST_COMPLETE 2
+#define IPW_CMD_POWER_DOWN 4
+#define IPW_CMD_SYSTEM_CONFIG 6
+#define IPW_CMD_MULTICAST_ADDRESS 7
+#define IPW_CMD_SSID 8
+#define IPW_CMD_ADAPTER_ADDRESS 11
+#define IPW_CMD_PORT_TYPE 12
+#define IPW_CMD_RTS_THRESHOLD 15
+#define IPW_CMD_FRAG_THRESHOLD 16
+#define IPW_CMD_POWER_MODE 17
+#define IPW_CMD_WEP_KEY 18
+#define IPW_CMD_TGI_TX_KEY 19
+#define IPW_CMD_SCAN_REQUEST 20
+#define IPW_CMD_ASSOCIATE 21
+#define IPW_CMD_SUPPORTED_RATES 22
+#define IPW_CMD_SCAN_ABORT 23
+#define IPW_CMD_TX_FLUSH 24
+#define IPW_CMD_QOS_PARAMETERS 25
+#define IPW_CMD_SCAN_REQUEST_EXT 26
+#define IPW_CMD_DINO_CONFIG 30
+#define IPW_CMD_RSN_CAPABILITIES 31
+#define IPW_CMD_RX_KEY 32
+#define IPW_CMD_CARD_DISABLE 33
+#define IPW_CMD_SEED_NUMBER 34
+#define IPW_CMD_TX_POWER 35
+#define IPW_CMD_COUNTRY_INFO 36
+#define IPW_CMD_AIRONET_INFO 37
+#define IPW_CMD_AP_TX_POWER 38
+#define IPW_CMD_CCKM_INFO 39
+#define IPW_CMD_CCX_VER_INFO 40
+#define IPW_CMD_SET_CALIBRATION 41
+#define IPW_CMD_SENSITIVITY_CALIB 42
+#define IPW_CMD_RETRY_LIMIT 51
+#define IPW_CMD_IPW_PRE_POWER_DOWN 58
+#define IPW_CMD_VAP_BEACON_TEMPLATE 60
+#define IPW_CMD_VAP_DTIM_PERIOD 61
+#define IPW_CMD_EXT_SUPPORTED_RATES 62
+#define IPW_CMD_VAP_LOCAL_TX_PWR_CONSTRAINT 63
+#define IPW_CMD_VAP_QUIET_INTERVALS 64
+#define IPW_CMD_VAP_CHANNEL_SWITCH 65
+#define IPW_CMD_VAP_MANDATORY_CHANNELS 66
+#define IPW_CMD_VAP_CELL_PWR_LIMIT 67
+#define IPW_CMD_VAP_CF_PARAM_SET 68
+#define IPW_CMD_VAP_SET_BEACONING_STATE 69
+#define IPW_CMD_MEASUREMENT 80
+#define IPW_CMD_POWER_CAPABILITY 81
+#define IPW_CMD_SUPPORTED_CHANNELS 82
+#define IPW_CMD_TPC_REPORT 83
+#define IPW_CMD_WME_INFO 84
+#define IPW_CMD_PRODUCTION_COMMAND 85
+#define IPW_CMD_LINKSYS_EOU_INFO 90
+
+#define RFD_SIZE 4
+#define NUM_TFD_CHUNKS 6
+
+#define TX_QUEUE_SIZE 32
+#define RX_QUEUE_SIZE 32
+
+#define DINO_CMD_WEP_KEY 0x08
+#define DINO_CMD_TX 0x0B
+#define DCT_ANTENNA_A 0x01
+#define DCT_ANTENNA_B 0x02
+
+#define IPW_A_MODE 0
+#define IPW_B_MODE 1
+#define IPW_G_MODE 2
+
+/*
+ * TX Queue Flag Definitions
+ */
+
+/* abort attempt if mgmt frame is rx'd */
+#define DCT_FLAG_ABORT_MGMT 0x01
+
+/* require CTS */
+#define DCT_FLAG_CTS_REQUIRED 0x02
+
+/* use short preamble */
+#define DCT_FLAG_SHORT_PREMBL 0x04
+
+/* RTS/CTS first */
+#define DCT_FLAG_RTS_REQD 0x08
+
+/* dont calculate duration field */
+#define DCT_FLAG_DUR_SET 0x10
+
+/* even if MAC WEP set (allows pre-encrypt) */
+#define DCT_FLAG_NO_WEP 0x20
+
+/* overwrite TSF field */
+#define DCT_FLAG_TSF_REQD 0x40
+
+/* ACK rx is expected to follow */
+#define DCT_FLAG_ACK_REQD 0x80
+
+#define DCT_FLAG_EXT_MODE_CCK 0x01
+#define DCT_FLAG_EXT_MODE_OFDM 0x00
+
+
+#define TX_RX_TYPE_MASK 0xFF
+#define TX_FRAME_TYPE 0x00
+#define TX_HOST_COMMAND_TYPE 0x01
+#define RX_FRAME_TYPE 0x09
+#define RX_HOST_NOTIFICATION_TYPE 0x03
+#define RX_HOST_CMD_RESPONSE_TYPE 0x04
+#define RX_TX_FRAME_RESPONSE_TYPE 0x05
+#define TFD_NEED_IRQ_MASK 0x04
+
+#define HOST_CMD_DINO_CONFIG 30
+
+#define HOST_NOTIFICATION_STATUS_ASSOCIATED 10
+#define HOST_NOTIFICATION_STATUS_AUTHENTICATE 11
+#define HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT 12
+#define HOST_NOTIFICATION_STATUS_SCAN_COMPLETED 13
+#define HOST_NOTIFICATION_STATUS_FRAG_LENGTH 14
+#define HOST_NOTIFICATION_STATUS_LINK_DETERIORATION 15
+#define HOST_NOTIFICATION_DINO_CONFIG_RESPONSE 16
+#define HOST_NOTIFICATION_STATUS_BEACON_STATE 17
+#define HOST_NOTIFICATION_STATUS_TGI_TX_KEY 18
+#define HOST_NOTIFICATION_TX_STATUS 19
+#define HOST_NOTIFICATION_CALIB_KEEP_RESULTS 20
+#define HOST_NOTIFICATION_MEASUREMENT_STARTED 21
+#define HOST_NOTIFICATION_MEASUREMENT_ENDED 22
+#define HOST_NOTIFICATION_CHANNEL_SWITCHED 23
+#define HOST_NOTIFICATION_RX_DURING_QUIET_PERIOD 24
+#define HOST_NOTIFICATION_NOISE_STATS 25
+#define HOST_NOTIFICATION_S36_MEASUREMENT_ACCEPTED 30
+#define HOST_NOTIFICATION_S36_MEASUREMENT_REFUSED 31
+
+#define HOST_NOTIFICATION_STATUS_BEACON_MISSING 1
+#define IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT 24
+#define IPW_MB_ROAMING_THRESHOLD_DEFAULT 8
+#define IPW_REAL_RATE_RX_PACKET_THRESHOLD 300
+
+#define MACADRR_BYTE_LEN 6
+
+#define DCR_TYPE_AP 0x01
+#define DCR_TYPE_WLAP 0x02
+#define DCR_TYPE_MU_ESS 0x03
+#define DCR_TYPE_MU_IBSS 0x04
+#define DCR_TYPE_MU_PIBSS 0x05
+#define DCR_TYPE_SNIFFER 0x06
+#define DCR_TYPE_MU_BSS DCR_TYPE_MU_ESS
+
+/**
+ * Generic queue structure
+ *
+ * Contains common data for Rx and Tx queues
+ */
+struct clx2_queue {
+ int n_bd; /**< number of BDs in this queue */
+ int first_empty; /**< 1-st empty entry (index) */
+ int last_used; /**< last used entry (index) */
+ u32 reg_w; /**< 'write' reg (queue head), addr in domain 1 */
+ u32 reg_r; /**< 'read' reg (queue tail), addr in domain 1 */
+ dma_addr_t dma_addr; /**< physical addr for BD's */
+ int low_mark; /**< low watermark, resume queue if free space more than this */
+ int high_mark; /**< high watermark, stop queue if free space less than this */
+} __attribute__ ((packed));
+
+struct machdr32
+{
+ u16 frame_ctl;
+ u16 duration; // watch out for endians!
+ u8 addr1[ MACADRR_BYTE_LEN ];
+ u8 addr2[ MACADRR_BYTE_LEN ];
+ u8 addr3[ MACADRR_BYTE_LEN ];
+ u16 seq_ctrl; // more endians!
+ u8 addr4[ MACADRR_BYTE_LEN ];
+ u16 qos_ctrl;
+} __attribute__ ((packed)) ;
+
+struct machdr30
+{
+ u16 frame_ctl;
+ u16 duration; // watch out for endians!
+ u8 addr1[ MACADRR_BYTE_LEN ];
+ u8 addr2[ MACADRR_BYTE_LEN ];
+ u8 addr3[ MACADRR_BYTE_LEN ];
+ u16 seq_ctrl; // more endians!
+ u8 addr4[ MACADRR_BYTE_LEN ];
+} __attribute__ ((packed)) ;
+
+struct machdr26
+{
+ u16 frame_ctl;
+ u16 duration; // watch out for endians!
+ u8 addr1[ MACADRR_BYTE_LEN ];
+ u8 addr2[ MACADRR_BYTE_LEN ];
+ u8 addr3[ MACADRR_BYTE_LEN ];
+ u16 seq_ctrl; // more endians!
+ u16 qos_ctrl;
+} __attribute__ ((packed)) ;
+
+struct machdr24
+{
+ u16 frame_ctl;
+ u16 duration; // watch out for endians!
+ u8 addr1[ MACADRR_BYTE_LEN ];
+ u8 addr2[ MACADRR_BYTE_LEN ];
+ u8 addr3[ MACADRR_BYTE_LEN ];
+ u16 seq_ctrl; // more endians!
+} __attribute__ ((packed)) ;
+
+// TX TFD with 32 byte MAC Header
+struct tx_tfd_32
+{
+ struct machdr32 mchdr; // 32
+ u32 uivplaceholder[2]; // 8
+} __attribute__ ((packed)) ;
+
+// TX TFD with 30 byte MAC Header
+struct tx_tfd_30
+{
+ struct machdr30 mchdr; // 30
+ u8 reserved[2]; // 2
+ u32 uivplaceholder[2]; // 8
+} __attribute__ ((packed)) ;
+
+// tx tfd with 26 byte mac header
+struct tx_tfd_26
+{
+ struct machdr26 mchdr; // 26
+ u8 reserved1[2]; // 2
+ u32 uivplaceholder[2]; // 8
+ u8 reserved2[4]; // 4
+} __attribute__ ((packed)) ;
+
+// tx tfd with 24 byte mac header
+struct tx_tfd_24
+{
+ struct machdr24 mchdr; // 24
+ u32 uivplaceholder[2]; // 8
+ u8 reserved[8]; // 8
+} __attribute__ ((packed)) ;
+
+
+#define DCT_WEP_KEY_FIELD_LENGTH 16
+
+struct tfd_command
+{
+ u8 index;
+ u8 length;
+ u16 reserved;
+ u8 payload[0];
+} __attribute__ ((packed)) ;
+
+struct tfd_data {
+ /* Header */
+ u32 work_area_ptr;
+ u8 station_number; /* 0 for BSS */
+ u8 reserved1;
+ u16 reserved2;
+
+ /* Tx Parameters */
+ u8 cmd_id;
+ u8 seq_num;
+ u16 len;
+ u8 priority;
+ u8 tx_flags;
+ u8 tx_flags_ext;
+ u8 key_index;
+ u8 wepkey[DCT_WEP_KEY_FIELD_LENGTH];
+ u8 rate;
+ u8 antenna;
+ u16 next_packet_duration;
+ u16 next_frag_len;
+ u16 back_off_counter; //////txop;
+ u8 retrylimit;
+ u16 cwcurrent;
+ u8 reserved3;
+
+ /* 802.11 MAC Header */
+ union
+ {
+ struct tx_tfd_24 tfd_24;
+ struct tx_tfd_26 tfd_26;
+ struct tx_tfd_30 tfd_30;
+ struct tx_tfd_32 tfd_32;
+ } tfd;
+
+ /* Payload DMA info */
+ u32 num_chunks;
+ u32 chunk_ptr[NUM_TFD_CHUNKS];
+ u16 chunk_len[NUM_TFD_CHUNKS];
+} __attribute__ ((packed));
+
+struct txrx_control_flags
+{
+ u8 message_type;
+ u8 rx_seq_num;
+ u8 control_bits;
+ u8 reserved;
+} __attribute__ ((packed));
+
+#define TFD_SIZE 128
+#define TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH (TFD_SIZE - sizeof(struct txrx_control_flags))
+
+struct tfd_frame
+{
+ struct txrx_control_flags control_flags;
+ union {
+ struct tfd_data data;
+ struct tfd_command cmd;
+ u8 raw[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH];
+ } u;
+} __attribute__ ((packed)) ;
+
+typedef void destructor_func(const void*);
+
+/**
+ * Tx Queue for DMA. Queue consists of circular buffer of
+ * BD's and required locking structures.
+ */
+struct clx2_tx_queue {
+ struct clx2_queue q;
+ struct tfd_frame* bd;
+ struct ieee80211_txb **txb;
+};
+
+/*
+ * RX related structures and functions
+ */
+#define RX_FREE_BUFFERS 32
+#define RX_LOW_WATERMARK 8
+
+#define SUP_RATE_11A_MAX_NUM_CHANNELS (8)
+#define SUP_RATE_11B_MAX_NUM_CHANNELS (4)
+#define SUP_RATE_11G_MAX_NUM_CHANNELS (12)
+
+// Used for passing to driver number of successes and failures per rate
+struct rate_histogram
+{
+ union {
+ u32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
+ u32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
+ u32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
+ } success;
+ union {
+ u32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
+ u32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
+ u32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
+ } failed;
+} __attribute__ ((packed));
+
+/* statistics command response */
+struct ipw_cmd_stats {
+ u8 cmd_id;
+ u8 seq_num;
+ u16 good_sfd;
+ u16 bad_plcp;
+ u16 wrong_bssid;
+ u16 valid_mpdu;
+ u16 bad_mac_header;
+ u16 reserved_frame_types;
+ u16 rx_ina;
+ u16 bad_crc32;
+ u16 invalid_cts;
+ u16 invalid_acks;
+ u16 long_distance_ina_fina;
+ u16 dsp_silence_unreachable;
+ u16 accumulated_rssi;
+ u16 rx_ovfl_frame_tossed;
+ u16 rssi_silence_threshold;
+ u16 rx_ovfl_frame_supplied;
+ u16 last_rx_frame_signal;
+ u16 last_rx_frame_noise;
+ u16 rx_autodetec_no_ofdm;
+ u16 rx_autodetec_no_barker;
+ u16 reserved;
+} __attribute__ ((packed));
+
+struct notif_channel_result {
+ u8 channel_num;
+ struct ipw_cmd_stats stats;
+ u8 uReserved;
+} __attribute__ ((packed));
+
+struct notif_scan_complete {
+ u8 scan_type;
+ u8 num_channels;
+ u8 status;
+ u8 reserved;
+} __attribute__ ((packed));
+
+struct notif_frag_length {
+ u16 frag_length;
+ u16 reserved;
+} __attribute__ ((packed));
+
+struct notif_beacon_state {
+ u32 state;
+ u32 number;
+} __attribute__ ((packed));
+
+struct notif_tgi_tx_key {
+ u8 key_state;
+ u8 security_type;
+ u8 station_index;
+ u8 reserved;
+} __attribute__ ((packed));
+
+struct notif_link_deterioration {
+ struct ipw_cmd_stats stats;
+ u8 rate;
+ u8 modulation;
+ struct rate_histogram histogram;
+ u8 reserved1;
+ u16 reserved2;
+} __attribute__ ((packed));
+
+struct notif_association {
+ u8 state;
+} __attribute__ ((packed));
+
+struct notif_authenticate {
+ u8 state;
+ struct machdr24 addr;
+ u16 status;
+} __attribute__ ((packed));
+
+struct notif_calibration {
+ u8 data[104];
+} __attribute__ ((packed));
+
+struct notif_noise {
+ u32 value;
+} __attribute__ ((packed));
+
+struct ipw_rx_notification {
+ u8 reserved[8];
+ u8 subtype;
+ u8 flags;
+ u16 size;
+ union {
+ struct notif_association assoc;
+ struct notif_authenticate auth;
+ struct notif_channel_result channel_result;
+ struct notif_scan_complete scan_complete;
+ struct notif_frag_length frag_len;
+ struct notif_beacon_state beacon_state;
+ struct notif_tgi_tx_key tgi_tx_key;
+ struct notif_link_deterioration link_deterioration;
+ struct notif_calibration calibration;
+ struct notif_noise noise;
+ u8 raw[0];
+ } u;
+} __attribute__ ((packed));
+
+struct ipw_rx_frame {
+ u32 reserved1;
+ u8 parent_tsf[4]; // fw_use[0] is boolean for OUR_TSF_IS_GREATER
+ u8 received_channel; // The channel that this frame was received on.
+ // Note that for .11b this does not have to be
+ // the same as the channel that it was sent.
+ // Filled by LMAC
+ u8 frameStatus;
+ u8 rate;
+ u8 rssi;
+ u8 agc;
+ u8 rssi_dbm;
+ u16 signal;
+ u16 noise;
+ u8 antennaAndPhy;
+ u8 control; // control bit should be on in bg
+ u8 rtscts_rate; // rate of rts or cts (in rts cts sequence rate
+ // is identical)
+ u8 rtscts_seen; // 0x1 RTS seen ; 0x2 CTS seen
+ u16 length;
+ u8 data[0];
+} __attribute__ ((packed));
+
+struct ipw_rx_header {
+ u8 message_type;
+ u8 rx_seq_num;
+ u8 control_bits;
+ u8 reserved;
+} __attribute__ ((packed));
+
+struct ipw_rx_packet
+{
+ struct ipw_rx_header header;
+ union {
+ struct ipw_rx_frame frame;
+ struct ipw_rx_notification notification;
+ } u;
+} __attribute__ ((packed));
+
+#define IPW_RX_NOTIFICATION_SIZE sizeof(struct ipw_rx_header) + 12
+#define IPW_RX_FRAME_SIZE sizeof(struct ipw_rx_header) + \
+ sizeof(struct ipw_rx_frame)
+
+struct ipw_rx_mem_buffer {
+ dma_addr_t dma_addr;
+ struct ipw_rx_buffer *rxb;
+ struct sk_buff *skb;
+ struct list_head list;
+}; /* Not transferred over network, so not __attribute__ ((packed)) */
+
+struct ipw_rx_queue {
+ struct ipw_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS];
+ struct ipw_rx_mem_buffer *queue[RX_QUEUE_SIZE];
+ u32 processed; /* Internal index to last handled Rx packet */
+ u32 read; /* Shared index to newest available Rx buffer */
+ u32 write; /* Shared index to oldest written Rx packet */
+ u32 free_count;/* Number of pre-allocated buffers in rx_free */
+ /* Each of these lists is used as a FIFO for ipw_rx_mem_buffers */
+ struct list_head rx_free; /* Own an SKBs */
+ struct list_head rx_used; /* No SKB allocated */
+ spinlock_t lock;
+}; /* Not transferred over network, so not __attribute__ ((packed)) */
+
+
+struct alive_command_responce {
+ u8 alive_command;
+ u8 sequence_number;
+ u16 software_revision;
+ u8 device_identifier;
+ u8 reserved1[5];
+ u16 reserved2;
+ u16 reserved3;
+ u16 clock_settle_time;
+ u16 powerup_settle_time;
+ u16 reserved4;
+ u8 time_stamp[5]; /* month, day, year, hours, minutes */
+ u8 ucode_valid;
+} __attribute__ ((packed));
+
+#define IPW_MAX_RATES 12
+
+struct ipw_rates {
+ u8 num_rates;
+ u8 rates[IPW_MAX_RATES];
+} __attribute__ ((packed));
+
+struct command_block
+{
+ unsigned int control;
+ u32 source_addr;
+ u32 dest_addr;
+ unsigned int status;
+} __attribute__ ((packed));
+
+#define CB_NUMBER_OF_ELEMENTS_SMALL 64
+struct fw_image_desc
+{
+ unsigned long last_cb_index;
+ unsigned long current_cb_index;
+ struct command_block cb_list[CB_NUMBER_OF_ELEMENTS_SMALL];
+ void * v_addr;
+ unsigned long p_addr;
+ unsigned long len;
+};
+
+struct ipw_sys_config
+{
+ u8 bt_coexistence;
+ u8 reserved1;
+ u8 answer_broadcast_ssid_probe;
+ u8 accept_all_data_frames;
+ u8 accept_non_directed_frames;
+ u8 exclude_unicast_unencrypted;
+ u8 disable_unicast_decryption;
+ u8 exclude_multicast_unencrypted;
+ u8 disable_multicast_decryption;
+ u8 antenna_diversity;
+ u8 pass_crc_to_host;
+ u8 dot11g_auto_detection;
+ u8 enable_cts_to_self;
+ u8 enable_multicast_filtering;
+ u8 bt_coexist_collision_thr;
+ u8 reserved2;
+ u8 accept_all_mgmt_bcpr;
+ u8 accept_all_mgtm_frames;
+ u8 pass_noise_stats_to_host;
+ u8 reserved3;
+} __attribute__ ((packed));
+
+struct ipw_multicast_addr
+{
+ u8 num_of_multicast_addresses;
+ u8 reserved[3];
+ u8 mac1[6];
+ u8 mac2[6];
+ u8 mac3[6];
+ u8 mac4[6];
+} __attribute__ ((packed));
+
+struct ipw_wep_key
+{
+ u8 cmd_id;
+ u8 seq_num;
+ u8 key_index;
+ u8 key_size;
+ u8 key[16];
+} __attribute__ ((packed));
+
+struct ipw_tgi_tx_key
+{
+ u8 key_id;
+ u8 security_type;
+ u8 station_index;
+ u8 flags;
+ u8 key[16];
+ u32 tx_counter[2];
+} __attribute__ ((packed));
+
+#define IPW_SCAN_CHANNELS 54
+
+struct ipw_scan_request
+{
+ u8 scan_type;
+ u16 dwell_time;
+ u8 channels_list[IPW_SCAN_CHANNELS];
+ u8 channels_reserved[3];
+} __attribute__ ((packed));
+
+enum {
+ IPW_SCAN_PASSIVE_TILL_FIRST_BEACON_SCAN = 0,
+ IPW_SCAN_PASSIVE_FULL_DWELL_SCAN,
+ IPW_SCAN_ACTIVE_DIRECT_SCAN,
+ IPW_SCAN_ACTIVE_BROADCAST_SCAN,
+ IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN,
+ IPW_SCAN_TYPES
+};
+
+struct ipw_scan_request_ext
+{
+ u32 full_scan_index;
+ u8 channels_list[IPW_SCAN_CHANNELS];
+ u8 scan_type[IPW_SCAN_CHANNELS / 2];
+ u8 reserved;
+ u16 dwell_time[IPW_SCAN_TYPES];
+} __attribute__ ((packed));
+
+extern inline u8 ipw_get_scan_type(struct ipw_scan_request_ext *scan, u8 index)
+{
+ if (index % 2)
+ return scan->scan_type[index / 2] & 0x0F;
+ else
+ return (scan->scan_type[index / 2] & 0xF0) >> 4;
+}
+
+extern inline void ipw_set_scan_type(struct ipw_scan_request_ext *scan,
+ u8 index, u8 scan_type)
+{
+ if (index % 2)
+ scan->scan_type[index / 2] =
+ (scan->scan_type[index / 2] & 0xF0) |
+ (scan_type & 0x0F);
+ else
+ scan->scan_type[index / 2] =
+ (scan->scan_type[index / 2] & 0x0F) |
+ ((scan_type & 0x0F) << 4);
+}
+
+struct ipw_associate
+{
+ u8 channel;
+ u8 auth_type:4,
+ auth_key:4;
+ u8 assoc_type;
+ u8 reserved;
+ u16 policy_support;
+ u8 preamble_length;
+ u8 ieee_mode;
+ u8 bssid[ETH_ALEN];
+ u32 assoc_tsf_msw;
+ u32 assoc_tsf_lsw;
+ u16 capability;
+ u16 listen_interval;
+ u16 beacon_interval;
+ u8 dest[ETH_ALEN];
+ u16 atim_window;
+ u8 smr;
+ u8 reserved1;
+ u16 reserved2;
+} __attribute__ ((packed));
+
+struct ipw_supported_rates
+{
+ u8 ieee_mode;
+ u8 num_rates;
+ u8 purpose;
+ u8 reserved;
+ u8 supported_rates[IPW_MAX_RATES];
+} __attribute__ ((packed));
+
+struct ipw_rts_threshold
+{
+ u16 rts_threshold;
+ u16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_frag_threshold
+{
+ u16 frag_threshold;
+ u16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_retry_limit
+{
+ u8 short_retry_limit;
+ u8 long_retry_limit;
+ u16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_dino_config
+{
+ u32 dino_config_addr;
+ u16 dino_config_size;
+ u8 dino_response;
+ u8 reserved;
+} __attribute__ ((packed));
+
+struct ipw_aironet_info
+{
+ u8 id;
+ u8 length;
+ u16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_rx_key
+{
+ u8 station_index;
+ u8 key_type;
+ u8 key_id;
+ u8 key_flag;
+ u8 key[16];
+ u8 station_address[6];
+ u8 key_index;
+ u8 reserved;
+} __attribute__ ((packed));
+
+struct ipw_country_channel_info
+{
+ u8 first_channel;
+ u8 no_channels;
+ s8 max_tx_power;
+} __attribute__ ((packed));
+
+struct ipw_country_info
+{
+ u8 id;
+ u8 length;
+ u8 country_str[3];
+ struct ipw_country_channel_info groups[7];
+} __attribute__ ((packed));
+
+struct ipw_channel_tx_power
+{
+ u8 channel_number;
+ s8 tx_power;
+} __attribute__ ((packed));
+
+#define SCAN_ASSOCIATED_INTERVAL (HZ)
+#define SCAN_INTERVAL (HZ / 10)
+#define MAX_A_CHANNELS 37
+#define MAX_B_CHANNELS 14
+
+struct ipw_tx_power
+{
+ u8 num_channels;
+ u8 ieee_mode;
+ struct ipw_channel_tx_power channels_tx_power[MAX_A_CHANNELS];
+} __attribute__ ((packed));
+
+struct ipw_qos_parameters
+{
+ u16 cw_min[4];
+ u16 cw_max[4];
+ u8 aifs[4];
+ u8 flag[4];
+ u16 tx_op_limit[4];
+} __attribute__ ((packed));
+
+struct ipw_rsn_capabilities
+{
+ u8 id;
+ u8 length;
+ u16 version;
+} __attribute__ ((packed));
+
+struct ipw_sensitivity_calib
+{
+ u16 beacon_rssi_raw;
+ u16 reserved;
+} __attribute__ ((packed));
+
+/**
+ * Host command structure.
+ *
+ * On input, the following fields should be filled:
+ * - cmd
+ * - len
+ * - status_len
+ * - param (if needed)
+ *
+ * On output,
+ * - \a status contains status;
+ * - \a param filled with status parameters.
+ */
+struct ipw_cmd {
+ u32 cmd; /**< Host command */
+ u32 status; /**< Status */
+ u32 status_len; /**< How many 32 bit parameters in the status */
+ u32 len; /**< incoming parameters length, bytes */
+ /**
+ * command parameters.
+ * There should be enough space for incoming and
+ * outcoming parameters.
+ * Incoming parameters listed 1-st, followed by outcoming params.
+ * nParams=(len+3)/4+status_len
+ */
+ u32 param[0];
+} __attribute__ ((packed));
+
+#define STATUS_HCMD_ACTIVE (1<<0) /**< host command in progress */
+
+#define STATUS_INT_ENABLED (1<<1)
+#define STATUS_RF_KILL_HW (1<<2)
+#define STATUS_RF_KILL_SW (1<<3)
+#define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW)
+
+#define STATUS_INIT (1<<5)
+#define STATUS_AUTH (1<<6)
+#define STATUS_ASSOCIATED (1<<7)
+#define STATUS_STATE_MASK (STATUS_INIT | STATUS_AUTH | STATUS_ASSOCIATED)
+
+#define STATUS_ASSOCIATING (1<<8)
+#define STATUS_DISASSOCIATING (1<<9)
+#define STATUS_ROAMING (1<<10)
+#define STATUS_EXIT_PENDING (1<<11)
+#define STATUS_DISASSOC_PENDING (1<<12)
+#define STATUS_STATE_PENDING (1<<13)
+
+#define STATUS_SCAN_PENDING (1<<20)
+#define STATUS_SCANNING (1<<21)
+#define STATUS_SCAN_ABORTING (1<<22)
+
+#define STATUS_INDIRECT_BYTE (1<<28) /* sysfs entry configured for access */
+#define STATUS_INDIRECT_DWORD (1<<29) /* sysfs entry configured for access */
+#define STATUS_DIRECT_DWORD (1<<30) /* sysfs entry configured for access */
+
+#define STATUS_SECURITY_UPDATED (1<<31) /* Security sync needed */
+
+#define CFG_STATIC_CHANNEL (1<<0) /* Restrict assoc. to single channel */
+#define CFG_STATIC_ESSID (1<<1) /* Restrict assoc. to single SSID */
+#define CFG_STATIC_BSSID (1<<2) /* Restrict assoc. to single BSSID */
+#define CFG_CUSTOM_MAC (1<<3)
+#define CFG_PREAMBLE (1<<4)
+#define CFG_ADHOC_PERSIST (1<<5)
+#define CFG_ASSOCIATE (1<<6)
+#define CFG_FIXED_RATE (1<<7)
+#define CFG_ADHOC_CREATE (1<<8)
+
+#define CAP_SHARED_KEY (1<<0) /* Off = OPEN */
+#define CAP_PRIVACY_ON (1<<1) /* Off = No privacy */
+
+#define MAX_STATIONS 32
+#define IPW_INVALID_STATION (0xff)
+
+struct ipw_station_entry {
+ u8 mac_addr[ETH_ALEN];
+ u8 reserved;
+ u8 support_mode;
+};
+
+#define AVG_ENTRIES 8
+struct average {
+ s16 entries[AVG_ENTRIES];
+ u8 pos;
+ u8 init;
+ s32 sum;
+};
+
+struct ipw_priv {
+ /* ieee device used by generic ieee processing code */
+ struct ieee80211_device *ieee;
+ struct ieee80211_security sec;
+
+ /* spinlock */
+ spinlock_t lock;
+
+ /* basic pci-network driver stuff */
+ struct pci_dev *pci_dev;
+ struct net_device *net_dev;
+
+ /* pci hardware address support */
+ void __iomem *hw_base;
+ unsigned long hw_len;
+
+ struct fw_image_desc sram_desc;
+
+ /* result of ucode download */
+ struct alive_command_responce dino_alive;
+
+ wait_queue_head_t wait_command_queue;
+ wait_queue_head_t wait_state;
+
+ /* Rx and Tx DMA processing queues */
+ struct ipw_rx_queue *rxq;
+ struct clx2_tx_queue txq_cmd;
+ struct clx2_tx_queue txq[4];
+ u32 status;
+ u32 config;
+ u32 capability;
+
+ u8 last_rx_rssi;
+ u8 last_noise;
+ struct average average_missed_beacons;
+ struct average average_rssi;
+ struct average average_noise;
+ u32 port_type;
+ int rx_bufs_min; /**< minimum number of bufs in Rx queue */
+ int rx_pend_max; /**< maximum pending buffers for one IRQ */
+ u32 hcmd_seq; /**< sequence number for hcmd */
+ u32 missed_beacon_threshold;
+ u32 roaming_threshold;
+
+ struct ipw_associate assoc_request;
+ struct ieee80211_network *assoc_network;
+
+ unsigned long ts_scan_abort;
+ struct ipw_supported_rates rates;
+ struct ipw_rates phy[3]; /**< PHY restrictions, per band */
+ struct ipw_rates supp; /**< software defined */
+ struct ipw_rates extended; /**< use for corresp. IE, AP only */
+
+ struct notif_link_deterioration last_link_deterioration; /** for statistics */
+ struct ipw_cmd* hcmd; /**< host command currently executed */
+
+ wait_queue_head_t hcmd_wq; /**< host command waits for execution */
+ u32 tsf_bcn[2]; /**< TSF from latest beacon */
+
+ struct notif_calibration calib; /**< last calibration */
+
+ /* ordinal interface with firmware */
+ u32 table0_addr;
+ u32 table0_len;
+ u32 table1_addr;
+ u32 table1_len;
+ u32 table2_addr;
+ u32 table2_len;
+
+ /* context information */
+ u8 essid[IW_ESSID_MAX_SIZE];
+ u8 essid_len;
+ u8 nick[IW_ESSID_MAX_SIZE];
+ u16 rates_mask;
+ u8 channel;
+ struct ipw_sys_config sys_config;
+ u32 power_mode;
+ u8 bssid[ETH_ALEN];
+ u16 rts_threshold;
+ u8 mac_addr[ETH_ALEN];
+ u8 num_stations;
+ u8 stations[MAX_STATIONS][ETH_ALEN];
+
+ u32 notif_missed_beacons;
+
+ /* Statistics and counters normalized with each association */
+ u32 last_missed_beacons;
+ u32 last_tx_packets;
+ u32 last_rx_packets;
+ u32 last_tx_failures;
+ u32 last_rx_err;
+ u32 last_rate;
+
+ u32 missed_adhoc_beacons;
+ u32 missed_beacons;
+ u32 rx_packets;
+ u32 tx_packets;
+ u32 quality;
+
+ /* eeprom */
+ u8 eeprom[0x100]; /* 256 bytes of eeprom */
+ int eeprom_delay;
+
+ struct iw_statistics wstats;
+
+ struct workqueue_struct *workqueue;
+
+ struct work_struct adhoc_check;
+ struct work_struct associate;
+ struct work_struct disassociate;
+ struct work_struct rx_replenish;
+ struct work_struct request_scan;
+ struct work_struct adapter_restart;
+ struct work_struct rf_kill;
+ struct work_struct up;
+ struct work_struct down;
+ struct work_struct gather_stats;
+ struct work_struct abort_scan;
+ struct work_struct roam;
+ struct work_struct scan_check;
+
+ struct tasklet_struct irq_tasklet;
+
+
+#define IPW_2200BG 1
+#define IPW_2915ABG 2
+ u8 adapter;
+
+#define IPW_DEFAULT_TX_POWER 0x14
+ u8 tx_power;
+
+#ifdef CONFIG_PM
+ u32 pm_state[16];
+#endif
+
+ /* network state */
+
+ /* Used to pass the current INTA value from ISR to Tasklet */
+ u32 isr_inta;
+
+ /* debugging info */
+ u32 indirect_dword;
+ u32 direct_dword;
+ u32 indirect_byte;
+}; /*ipw_priv */
+
+
+/* debug macros */
+
+#ifdef CONFIG_IPW_DEBUG
+#define IPW_DEBUG(level, fmt, args...) \
+do { if (ipw_debug_level & (level)) \
+ printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \
+ in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0)
+#else
+#define IPW_DEBUG(level, fmt, args...) do {} while (0)
+#endif /* CONFIG_IPW_DEBUG */
+
+/*
+ * To use the debug system;
+ *
+ * If you are defining a new debug classification, simply add it to the #define
+ * list here in the form of:
+ *
+ * #define IPW_DL_xxxx VALUE
+ *
+ * shifting value to the left one bit from the previous entry. xxxx should be
+ * the name of the classification (for example, WEP)
+ *
+ * You then need to either add a IPW_xxxx_DEBUG() macro definition for your
+ * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want
+ * to send output to that classification.
+ *
+ * To add your debug level to the list of levels seen when you perform
+ *
+ * % cat /proc/net/ipw/debug_level
+ *
+ * you simply need to add your entry to the ipw_debug_levels array.
+ *
+ * If you do not see debug_level in /proc/net/ipw then you do not have
+ * CONFIG_IPW_DEBUG defined in your kernel configuration
+ *
+ */
+
+#define IPW_DL_ERROR (1<<0)
+#define IPW_DL_WARNING (1<<1)
+#define IPW_DL_INFO (1<<2)
+#define IPW_DL_WX (1<<3)
+#define IPW_DL_HOST_COMMAND (1<<5)
+#define IPW_DL_STATE (1<<6)
+
+#define IPW_DL_NOTIF (1<<10)
+#define IPW_DL_SCAN (1<<11)
+#define IPW_DL_ASSOC (1<<12)
+#define IPW_DL_DROP (1<<13)
+#define IPW_DL_IOCTL (1<<14)
+
+#define IPW_DL_MANAGE (1<<15)
+#define IPW_DL_FW (1<<16)
+#define IPW_DL_RF_KILL (1<<17)
+#define IPW_DL_FW_ERRORS (1<<18)
+
+
+#define IPW_DL_ORD (1<<20)
+
+#define IPW_DL_FRAG (1<<21)
+#define IPW_DL_WEP (1<<22)
+#define IPW_DL_TX (1<<23)
+#define IPW_DL_RX (1<<24)
+#define IPW_DL_ISR (1<<25)
+#define IPW_DL_FW_INFO (1<<26)
+#define IPW_DL_IO (1<<27)
+#define IPW_DL_TRACE (1<<28)
+
+#define IPW_DL_STATS (1<<29)
+
+
+#define IPW_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a)
+#define IPW_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a)
+#define IPW_DEBUG_INFO(f, a...) IPW_DEBUG(IPW_DL_INFO, f, ## a)
+
+#define IPW_DEBUG_WX(f, a...) IPW_DEBUG(IPW_DL_WX, f, ## a)
+#define IPW_DEBUG_SCAN(f, a...) IPW_DEBUG(IPW_DL_SCAN, f, ## a)
+#define IPW_DEBUG_STATUS(f, a...) IPW_DEBUG(IPW_DL_STATUS, f, ## a)
+#define IPW_DEBUG_TRACE(f, a...) IPW_DEBUG(IPW_DL_TRACE, f, ## a)
+#define IPW_DEBUG_RX(f, a...) IPW_DEBUG(IPW_DL_RX, f, ## a)
+#define IPW_DEBUG_TX(f, a...) IPW_DEBUG(IPW_DL_TX, f, ## a)
+#define IPW_DEBUG_ISR(f, a...) IPW_DEBUG(IPW_DL_ISR, f, ## a)
+#define IPW_DEBUG_MANAGEMENT(f, a...) IPW_DEBUG(IPW_DL_MANAGE, f, ## a)
+#define IPW_DEBUG_WEP(f, a...) IPW_DEBUG(IPW_DL_WEP, f, ## a)
+#define IPW_DEBUG_HC(f, a...) IPW_DEBUG(IPW_DL_HOST_COMMAND, f, ## a)
+#define IPW_DEBUG_FRAG(f, a...) IPW_DEBUG(IPW_DL_FRAG, f, ## a)
+#define IPW_DEBUG_FW(f, a...) IPW_DEBUG(IPW_DL_FW, f, ## a)
+#define IPW_DEBUG_RF_KILL(f, a...) IPW_DEBUG(IPW_DL_RF_KILL, f, ## a)
+#define IPW_DEBUG_DROP(f, a...) IPW_DEBUG(IPW_DL_DROP, f, ## a)
+#define IPW_DEBUG_IO(f, a...) IPW_DEBUG(IPW_DL_IO, f, ## a)
+#define IPW_DEBUG_ORD(f, a...) IPW_DEBUG(IPW_DL_ORD, f, ## a)
+#define IPW_DEBUG_FW_INFO(f, a...) IPW_DEBUG(IPW_DL_FW_INFO, f, ## a)
+#define IPW_DEBUG_NOTIF(f, a...) IPW_DEBUG(IPW_DL_NOTIF, f, ## a)
+#define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
+#define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
+#define IPW_DEBUG_STATS(f, a...) IPW_DEBUG(IPW_DL_STATS, f, ## a)
+
+#include <linux/ctype.h>
+
+/*
+* Register bit definitions
+*/
+
+/* Dino control registers bits */
+
+#define DINO_ENABLE_SYSTEM 0x80
+#define DINO_ENABLE_CS 0x40
+#define DINO_RXFIFO_DATA 0x01
+#define DINO_CONTROL_REG 0x00200000
+
+#define CX2_INTA_RW 0x00000008
+#define CX2_INTA_MASK_R 0x0000000C
+#define CX2_INDIRECT_ADDR 0x00000010
+#define CX2_INDIRECT_DATA 0x00000014
+#define CX2_AUTOINC_ADDR 0x00000018
+#define CX2_AUTOINC_DATA 0x0000001C
+#define CX2_RESET_REG 0x00000020
+#define CX2_GP_CNTRL_RW 0x00000024
+
+#define CX2_READ_INT_REGISTER 0xFF4
+
+#define CX2_GP_CNTRL_BIT_INIT_DONE 0x00000004
+
+#define CX2_REGISTER_DOMAIN1_END 0x00001000
+#define CX2_SRAM_READ_INT_REGISTER 0x00000ff4
+
+#define CX2_SHARED_LOWER_BOUND 0x00000200
+#define CX2_INTERRUPT_AREA_LOWER_BOUND 0x00000f80
+
+#define CX2_NIC_SRAM_LOWER_BOUND 0x00000000
+#define CX2_NIC_SRAM_UPPER_BOUND 0x00030000
+
+#define CX2_BIT_INT_HOST_SRAM_READ_INT_REGISTER (1 << 29)
+#define CX2_GP_CNTRL_BIT_CLOCK_READY 0x00000001
+#define CX2_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY 0x00000002
+
+/*
+ * RESET Register Bit Indexes
+ */
+#define CBD_RESET_REG_PRINCETON_RESET 0x00000001 /* Bit 0 (LSB) */
+#define CX2_RESET_REG_SW_RESET 0x00000080 /* Bit 7 */
+#define CX2_RESET_REG_MASTER_DISABLED 0x00000100 /* Bit 8 */
+#define CX2_RESET_REG_STOP_MASTER 0x00000200 /* Bit 9 */
+#define CX2_ARC_KESHET_CONFIG 0x08000000 /* Bit 27 */
+#define CX2_START_STANDBY 0x00000004 /* Bit 2 */
+
+#define CX2_CSR_CIS_UPPER_BOUND 0x00000200
+#define CX2_DOMAIN_0_END 0x1000
+#define CLX_MEM_BAR_SIZE 0x1000
+
+#define CX2_BASEBAND_CONTROL_STATUS 0X00200000
+#define CX2_BASEBAND_TX_FIFO_WRITE 0X00200004
+#define CX2_BASEBAND_RX_FIFO_READ 0X00200004
+#define CX2_BASEBAND_CONTROL_STORE 0X00200010
+
+#define CX2_INTERNAL_CMD_EVENT 0X00300004
+#define CX2_BASEBAND_POWER_DOWN 0x00000001
+
+#define CX2_MEM_HALT_AND_RESET 0x003000e0
+
+/* defgroup bits_halt_reset MEM_HALT_AND_RESET register bits */
+#define CX2_BIT_HALT_RESET_ON 0x80000000
+#define CX2_BIT_HALT_RESET_OFF 0x00000000
+
+#define CB_LAST_VALID 0x20000000
+#define CB_INT_ENABLED 0x40000000
+#define CB_VALID 0x80000000
+#define CB_SRC_LE 0x08000000
+#define CB_DEST_LE 0x04000000
+#define CB_SRC_AUTOINC 0x00800000
+#define CB_SRC_IO_GATED 0x00400000
+#define CB_DEST_AUTOINC 0x00080000
+#define CB_SRC_SIZE_LONG 0x00200000
+#define CB_DEST_SIZE_LONG 0x00020000
+
+
+/* DMA DEFINES */
+
+#define DMA_CONTROL_SMALL_CB_CONST_VALUE 0x00540000
+#define DMA_CB_STOP_AND_ABORT 0x00000C00
+#define DMA_CB_START 0x00000100
+
+
+#define CX2_SHARED_SRAM_SIZE 0x00030000
+#define CX2_SHARED_SRAM_DMA_CONTROL 0x00027000
+#define CB_MAX_LENGTH 0x1FFF
+
+#define CX2_HOST_EEPROM_DATA_SRAM_SIZE 0xA18
+#define CX2_EEPROM_IMAGE_SIZE 0x100
+
+
+/* DMA defs */
+#define CX2_DMA_I_CURRENT_CB 0x003000D0
+#define CX2_DMA_O_CURRENT_CB 0x003000D4
+#define CX2_DMA_I_DMA_CONTROL 0x003000A4
+#define CX2_DMA_I_CB_BASE 0x003000A0
+
+#define CX2_TX_CMD_QUEUE_BD_BASE (0x00000200)
+#define CX2_TX_CMD_QUEUE_BD_SIZE (0x00000204)
+#define CX2_TX_QUEUE_0_BD_BASE (0x00000208)
+#define CX2_TX_QUEUE_0_BD_SIZE (0x0000020C)
+#define CX2_TX_QUEUE_1_BD_BASE (0x00000210)
+#define CX2_TX_QUEUE_1_BD_SIZE (0x00000214)
+#define CX2_TX_QUEUE_2_BD_BASE (0x00000218)
+#define CX2_TX_QUEUE_2_BD_SIZE (0x0000021C)
+#define CX2_TX_QUEUE_3_BD_BASE (0x00000220)
+#define CX2_TX_QUEUE_3_BD_SIZE (0x00000224)
+#define CX2_RX_BD_BASE (0x00000240)
+#define CX2_RX_BD_SIZE (0x00000244)
+#define CX2_RFDS_TABLE_LOWER (0x00000500)
+
+#define CX2_TX_CMD_QUEUE_READ_INDEX (0x00000280)
+#define CX2_TX_QUEUE_0_READ_INDEX (0x00000284)
+#define CX2_TX_QUEUE_1_READ_INDEX (0x00000288)
+#define CX2_TX_QUEUE_2_READ_INDEX (0x0000028C)
+#define CX2_TX_QUEUE_3_READ_INDEX (0x00000290)
+#define CX2_RX_READ_INDEX (0x000002A0)
+
+#define CX2_TX_CMD_QUEUE_WRITE_INDEX (0x00000F80)
+#define CX2_TX_QUEUE_0_WRITE_INDEX (0x00000F84)
+#define CX2_TX_QUEUE_1_WRITE_INDEX (0x00000F88)
+#define CX2_TX_QUEUE_2_WRITE_INDEX (0x00000F8C)
+#define CX2_TX_QUEUE_3_WRITE_INDEX (0x00000F90)
+#define CX2_RX_WRITE_INDEX (0x00000FA0)
+
+/*
+ * EEPROM Related Definitions
+ */
+
+#define IPW_EEPROM_DATA_SRAM_ADDRESS (CX2_SHARED_LOWER_BOUND + 0x814)
+#define IPW_EEPROM_DATA_SRAM_SIZE (CX2_SHARED_LOWER_BOUND + 0x818)
+#define IPW_EEPROM_LOAD_DISABLE (CX2_SHARED_LOWER_BOUND + 0x81C)
+#define IPW_EEPROM_DATA (CX2_SHARED_LOWER_BOUND + 0x820)
+#define IPW_EEPROM_UPPER_ADDRESS (CX2_SHARED_LOWER_BOUND + 0x9E0)
+
+#define IPW_STATION_TABLE_LOWER (CX2_SHARED_LOWER_BOUND + 0xA0C)
+#define IPW_STATION_TABLE_UPPER (CX2_SHARED_LOWER_BOUND + 0xB0C)
+#define IPW_REQUEST_ATIM (CX2_SHARED_LOWER_BOUND + 0xB0C)
+#define IPW_ATIM_SENT (CX2_SHARED_LOWER_BOUND + 0xB10)
+#define IPW_WHO_IS_AWAKE (CX2_SHARED_LOWER_BOUND + 0xB14)
+#define IPW_DURING_ATIM_WINDOW (CX2_SHARED_LOWER_BOUND + 0xB18)
+
+
+#define MSB 1
+#define LSB 0
+#define WORD_TO_BYTE(_word) ((_word) * sizeof(u16))
+
+#define GET_EEPROM_ADDR(_wordoffset,_byteoffset) \
+ ( WORD_TO_BYTE(_wordoffset) + (_byteoffset) )
+
+/* EEPROM access by BYTE */
+#define EEPROM_PME_CAPABILITY (GET_EEPROM_ADDR(0x09,MSB)) /* 1 byte */
+#define EEPROM_MAC_ADDRESS (GET_EEPROM_ADDR(0x21,LSB)) /* 6 byte */
+#define EEPROM_VERSION (GET_EEPROM_ADDR(0x24,MSB)) /* 1 byte */
+#define EEPROM_NIC_TYPE (GET_EEPROM_ADDR(0x25,LSB)) /* 1 byte */
+#define EEPROM_SKU_CAPABILITY (GET_EEPROM_ADDR(0x25,MSB)) /* 1 byte */
+#define EEPROM_COUNTRY_CODE (GET_EEPROM_ADDR(0x26,LSB)) /* 3 bytes */
+#define EEPROM_IBSS_CHANNELS_BG (GET_EEPROM_ADDR(0x28,LSB)) /* 2 bytes */
+#define EEPROM_IBSS_CHANNELS_A (GET_EEPROM_ADDR(0x29,MSB)) /* 5 bytes */
+#define EEPROM_BSS_CHANNELS_BG (GET_EEPROM_ADDR(0x2c,LSB)) /* 2 bytes */
+#define EEPROM_HW_VERSION (GET_EEPROM_ADDR(0x72,LSB)) /* 2 bytes */
+
+/* NIC type as found in the one byte EEPROM_NIC_TYPE offset*/
+#define EEPROM_NIC_TYPE_STANDARD 0
+#define EEPROM_NIC_TYPE_DELL 1
+#define EEPROM_NIC_TYPE_FUJITSU 2
+#define EEPROM_NIC_TYPE_IBM 3
+#define EEPROM_NIC_TYPE_HP 4
+
+#define FW_MEM_REG_LOWER_BOUND 0x00300000
+#define FW_MEM_REG_EEPROM_ACCESS (FW_MEM_REG_LOWER_BOUND + 0x40)
+
+#define EEPROM_BIT_SK (1<<0)
+#define EEPROM_BIT_CS (1<<1)
+#define EEPROM_BIT_DI (1<<2)
+#define EEPROM_BIT_DO (1<<4)
+
+#define EEPROM_CMD_READ 0x2
+
+/* Interrupts masks */
+#define CX2_INTA_NONE 0x00000000
+
+#define CX2_INTA_BIT_RX_TRANSFER 0x00000002
+#define CX2_INTA_BIT_STATUS_CHANGE 0x00000010
+#define CX2_INTA_BIT_BEACON_PERIOD_EXPIRED 0x00000020
+
+//Inta Bits for CF
+#define CX2_INTA_BIT_TX_CMD_QUEUE 0x00000800
+#define CX2_INTA_BIT_TX_QUEUE_1 0x00001000
+#define CX2_INTA_BIT_TX_QUEUE_2 0x00002000
+#define CX2_INTA_BIT_TX_QUEUE_3 0x00004000
+#define CX2_INTA_BIT_TX_QUEUE_4 0x00008000
+
+#define CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE 0x00010000
+
+#define CX2_INTA_BIT_PREPARE_FOR_POWER_DOWN 0x00100000
+#define CX2_INTA_BIT_POWER_DOWN 0x00200000
+
+#define CX2_INTA_BIT_FW_INITIALIZATION_DONE 0x01000000
+#define CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE 0x02000000
+#define CX2_INTA_BIT_RF_KILL_DONE 0x04000000
+#define CX2_INTA_BIT_FATAL_ERROR 0x40000000
+#define CX2_INTA_BIT_PARITY_ERROR 0x80000000
+
+/* Interrupts enabled at init time. */
+#define CX2_INTA_MASK_ALL \
+ (CX2_INTA_BIT_TX_QUEUE_1 | \
+ CX2_INTA_BIT_TX_QUEUE_2 | \
+ CX2_INTA_BIT_TX_QUEUE_3 | \
+ CX2_INTA_BIT_TX_QUEUE_4 | \
+ CX2_INTA_BIT_TX_CMD_QUEUE | \
+ CX2_INTA_BIT_RX_TRANSFER | \
+ CX2_INTA_BIT_FATAL_ERROR | \
+ CX2_INTA_BIT_PARITY_ERROR | \
+ CX2_INTA_BIT_STATUS_CHANGE | \
+ CX2_INTA_BIT_FW_INITIALIZATION_DONE | \
+ CX2_INTA_BIT_BEACON_PERIOD_EXPIRED | \
+ CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE | \
+ CX2_INTA_BIT_PREPARE_FOR_POWER_DOWN | \
+ CX2_INTA_BIT_POWER_DOWN | \
+ CX2_INTA_BIT_RF_KILL_DONE )
+
+#define IPWSTATUS_ERROR_LOG (CX2_SHARED_LOWER_BOUND + 0x410)
+#define IPW_EVENT_LOG (CX2_SHARED_LOWER_BOUND + 0x414)
+
+/* FW event log definitions */
+#define EVENT_ELEM_SIZE (3 * sizeof(u32))
+#define EVENT_START_OFFSET (1 * sizeof(u32) + 2 * sizeof(u16))
+
+/* FW error log definitions */
+#define ERROR_ELEM_SIZE (7 * sizeof(u32))
+#define ERROR_START_OFFSET (1 * sizeof(u32))
+
+enum {
+ IPW_FW_ERROR_OK = 0,
+ IPW_FW_ERROR_FAIL,
+ IPW_FW_ERROR_MEMORY_UNDERFLOW,
+ IPW_FW_ERROR_MEMORY_OVERFLOW,
+ IPW_FW_ERROR_BAD_PARAM,
+ IPW_FW_ERROR_BAD_CHECKSUM,
+ IPW_FW_ERROR_NMI_INTERRUPT,
+ IPW_FW_ERROR_BAD_DATABASE,
+ IPW_FW_ERROR_ALLOC_FAIL,
+ IPW_FW_ERROR_DMA_UNDERRUN,
+ IPW_FW_ERROR_DMA_STATUS,
+ IPW_FW_ERROR_DINOSTATUS_ERROR,
+ IPW_FW_ERROR_EEPROMSTATUS_ERROR,
+ IPW_FW_ERROR_SYSASSERT,
+ IPW_FW_ERROR_FATAL_ERROR
+};
+
+#define AUTH_OPEN 0
+#define AUTH_SHARED_KEY 1
+#define AUTH_IGNORE 3
+
+#define HC_ASSOCIATE 0
+#define HC_REASSOCIATE 1
+#define HC_DISASSOCIATE 2
+#define HC_IBSS_START 3
+#define HC_IBSS_RECONF 4
+#define HC_DISASSOC_QUIET 5
+
+#define IPW_RATE_CAPABILITIES 1
+#define IPW_RATE_CONNECT 0
+
+
+/*
+ * Rate values and masks
+ */
+#define IPW_TX_RATE_1MB 0x0A
+#define IPW_TX_RATE_2MB 0x14
+#define IPW_TX_RATE_5MB 0x37
+#define IPW_TX_RATE_6MB 0x0D
+#define IPW_TX_RATE_9MB 0x0F
+#define IPW_TX_RATE_11MB 0x6E
+#define IPW_TX_RATE_12MB 0x05
+#define IPW_TX_RATE_18MB 0x07
+#define IPW_TX_RATE_24MB 0x09
+#define IPW_TX_RATE_36MB 0x0B
+#define IPW_TX_RATE_48MB 0x01
+#define IPW_TX_RATE_54MB 0x03
+
+#define IPW_ORD_TABLE_ID_MASK 0x0000FF00
+#define IPW_ORD_TABLE_VALUE_MASK 0x000000FF
+
+#define IPW_ORD_TABLE_0_MASK 0x0000F000
+#define IPW_ORD_TABLE_1_MASK 0x0000F100
+#define IPW_ORD_TABLE_2_MASK 0x0000F200
+#define IPW_ORD_TABLE_3_MASK 0x0000F300
+#define IPW_ORD_TABLE_4_MASK 0x0000F400
+#define IPW_ORD_TABLE_5_MASK 0x0000F500
+#define IPW_ORD_TABLE_6_MASK 0x0000F600
+#define IPW_ORD_TABLE_7_MASK 0x0000F700
+
+/*
+ * Table 0 Entries (all entries are 32 bits)
+ */
+enum {
+ IPW_ORD_STAT_TX_CURR_RATE = IPW_ORD_TABLE_0_MASK + 1,
+ IPW_ORD_STAT_FRAG_TRESHOLD,
+ IPW_ORD_STAT_RTS_THRESHOLD,
+ IPW_ORD_STAT_TX_HOST_REQUESTS,
+ IPW_ORD_STAT_TX_HOST_COMPLETE,
+ IPW_ORD_STAT_TX_DIR_DATA,
+ IPW_ORD_STAT_TX_DIR_DATA_B_1,
+ IPW_ORD_STAT_TX_DIR_DATA_B_2,
+ IPW_ORD_STAT_TX_DIR_DATA_B_5_5,
+ IPW_ORD_STAT_TX_DIR_DATA_B_11,
+ /* Hole */
+
+
+
+
+
+
+
+ IPW_ORD_STAT_TX_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 19,
+ IPW_ORD_STAT_TX_DIR_DATA_G_2,
+ IPW_ORD_STAT_TX_DIR_DATA_G_5_5,
+ IPW_ORD_STAT_TX_DIR_DATA_G_6,
+ IPW_ORD_STAT_TX_DIR_DATA_G_9,
+ IPW_ORD_STAT_TX_DIR_DATA_G_11,
+ IPW_ORD_STAT_TX_DIR_DATA_G_12,
+ IPW_ORD_STAT_TX_DIR_DATA_G_18,
+ IPW_ORD_STAT_TX_DIR_DATA_G_24,
+ IPW_ORD_STAT_TX_DIR_DATA_G_36,
+ IPW_ORD_STAT_TX_DIR_DATA_G_48,
+ IPW_ORD_STAT_TX_DIR_DATA_G_54,
+ IPW_ORD_STAT_TX_NON_DIR_DATA,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_B_1,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_B_2,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_B_5_5,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_B_11,
+ /* Hole */
+
+
+
+
+
+
+
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 44,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_2,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_5_5,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_6,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_9,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_11,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_12,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_18,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_24,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_36,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_48,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_54,
+ IPW_ORD_STAT_TX_RETRY,
+ IPW_ORD_STAT_TX_FAILURE,
+ IPW_ORD_STAT_RX_ERR_CRC,
+ IPW_ORD_STAT_RX_ERR_ICV,
+ IPW_ORD_STAT_RX_NO_BUFFER,
+ IPW_ORD_STAT_FULL_SCANS,
+ IPW_ORD_STAT_PARTIAL_SCANS,
+ IPW_ORD_STAT_TGH_ABORTED_SCANS,
+ IPW_ORD_STAT_TX_TOTAL_BYTES,
+ IPW_ORD_STAT_CURR_RSSI_RAW,
+ IPW_ORD_STAT_RX_BEACON,
+ IPW_ORD_STAT_MISSED_BEACONS,
+ IPW_ORD_TABLE_0_LAST
+};
+
+#define IPW_RSSI_TO_DBM 112
+
+/* Table 1 Entries
+ */
+enum {
+ IPW_ORD_TABLE_1_LAST = IPW_ORD_TABLE_1_MASK | 1,
+};
+
+/*
+ * Table 2 Entries
+ *
+ * FW_VERSION: 16 byte string
+ * FW_DATE: 16 byte string (only 14 bytes used)
+ * UCODE_VERSION: 4 byte version code
+ * UCODE_DATE: 5 bytes code code
+ * ADDAPTER_MAC: 6 byte MAC address
+ * RTC: 4 byte clock
+ */
+enum {
+ IPW_ORD_STAT_FW_VERSION = IPW_ORD_TABLE_2_MASK | 1,
+ IPW_ORD_STAT_FW_DATE,
+ IPW_ORD_STAT_UCODE_VERSION,
+ IPW_ORD_STAT_UCODE_DATE,
+ IPW_ORD_STAT_ADAPTER_MAC,
+ IPW_ORD_STAT_RTC,
+ IPW_ORD_TABLE_2_LAST
+};
+
+/* Table 3 */
+enum {
+ IPW_ORD_STAT_TX_PACKET = IPW_ORD_TABLE_3_MASK | 0,
+ IPW_ORD_STAT_TX_PACKET_FAILURE,
+ IPW_ORD_STAT_TX_PACKET_SUCCESS,
+ IPW_ORD_STAT_TX_PACKET_ABORTED,
+ IPW_ORD_TABLE_3_LAST
+};
+
+/* Table 4 */
+enum {
+ IPW_ORD_TABLE_4_LAST = IPW_ORD_TABLE_4_MASK
+};
+
+/* Table 5 */
+enum {
+ IPW_ORD_STAT_AVAILABLE_AP_COUNT = IPW_ORD_TABLE_5_MASK,
+ IPW_ORD_STAT_AP_ASSNS,
+ IPW_ORD_STAT_ROAM,
+ IPW_ORD_STAT_ROAM_CAUSE_MISSED_BEACONS,
+ IPW_ORD_STAT_ROAM_CAUSE_UNASSOC,
+ IPW_ORD_STAT_ROAM_CAUSE_RSSI,
+ IPW_ORD_STAT_ROAM_CAUSE_LINK_QUALITY,
+ IPW_ORD_STAT_ROAM_CAUSE_AP_LOAD_BALANCE,
+ IPW_ORD_STAT_ROAM_CAUSE_AP_NO_TX,
+ IPW_ORD_STAT_LINK_UP,
+ IPW_ORD_STAT_LINK_DOWN,
+ IPW_ORD_ANTENNA_DIVERSITY,
+ IPW_ORD_CURR_FREQ,
+ IPW_ORD_TABLE_5_LAST
+};
+
+/* Table 6 */
+enum {
+ IPW_ORD_COUNTRY_CODE = IPW_ORD_TABLE_6_MASK,
+ IPW_ORD_CURR_BSSID,
+ IPW_ORD_CURR_SSID,
+ IPW_ORD_TABLE_6_LAST
+};
+
+/* Table 7 */
+enum {
+ IPW_ORD_STAT_PERCENT_MISSED_BEACONS = IPW_ORD_TABLE_7_MASK,
+ IPW_ORD_STAT_PERCENT_TX_RETRIES,
+ IPW_ORD_STAT_PERCENT_LINK_QUALITY,
+ IPW_ORD_STAT_CURR_RSSI_DBM,
+ IPW_ORD_TABLE_7_LAST
+};
+
+#define IPW_ORDINALS_TABLE_LOWER (CX2_SHARED_LOWER_BOUND + 0x500)
+#define IPW_ORDINALS_TABLE_0 (CX2_SHARED_LOWER_BOUND + 0x180)
+#define IPW_ORDINALS_TABLE_1 (CX2_SHARED_LOWER_BOUND + 0x184)
+#define IPW_ORDINALS_TABLE_2 (CX2_SHARED_LOWER_BOUND + 0x188)
+#define IPW_MEM_FIXED_OVERRIDE (CX2_SHARED_LOWER_BOUND + 0x41C)
+
+struct ipw_fixed_rate {
+ u16 tx_rates;
+ u16 reserved;
+} __attribute__ ((packed));
+
+#define CX2_INDIRECT_ADDR_MASK (~0x3ul)
+
+struct host_cmd {
+ u8 cmd;
+ u8 len;
+ u16 reserved;
+ u32 param[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH];
+} __attribute__ ((packed));
+
+#define CFG_BT_COEXISTENCE_MIN 0x00
+#define CFG_BT_COEXISTENCE_DEFER 0x02
+#define CFG_BT_COEXISTENCE_KILL 0x04
+#define CFG_BT_COEXISTENCE_WME_OVER_BT 0x08
+#define CFG_BT_COEXISTENCE_OOB 0x10
+#define CFG_BT_COEXISTENCE_MAX 0xFF
+#define CFG_BT_COEXISTENCE_DEF 0x80 /* read Bt from EEPROM*/
+
+#define CFG_CTS_TO_ITSELF_ENABLED_MIN 0x0
+#define CFG_CTS_TO_ITSELF_ENABLED_MAX 0x1
+#define CFG_CTS_TO_ITSELF_ENABLED_DEF CFG_CTS_TO_ITSELF_ENABLED_MIN
+
+#define CFG_SYS_ANTENNA_BOTH 0x000
+#define CFG_SYS_ANTENNA_A 0x001
+#define CFG_SYS_ANTENNA_B 0x003
+
+/*
+ * The definitions below were lifted off the ipw2100 driver, which only
+ * supports 'b' mode, so I'm sure these are not exactly correct.
+ *
+ * Somebody fix these!!
+ */
+#define REG_MIN_CHANNEL 0
+#define REG_MAX_CHANNEL 14
+
+#define REG_CHANNEL_MASK 0x00003FFF
+#define IPW_IBSS_11B_DEFAULT_MASK 0x87ff
+
+static const long ipw_frequencies[] = {
+ 2412, 2417, 2422, 2427,
+ 2432, 2437, 2442, 2447,
+ 2452, 2457, 2462, 2467,
+ 2472, 2484
+};
+
+#define FREQ_COUNT ARRAY_SIZE(ipw_frequencies)
+
+#define IPW_MAX_CONFIG_RETRIES 10
+
+static inline u32 frame_hdr_len(struct ieee80211_hdr *hdr)
+{
+ u32 retval;
+ u16 fc;
+
+ retval = sizeof(struct ieee80211_hdr);
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ /*
+ * Function ToDS FromDS
+ * IBSS 0 0
+ * To AP 1 0
+ * From AP 0 1
+ * WDS (bridge) 1 1
+ *
+ * Only WDS frames use Address4 among them. --YZ
+ */
+ if (!(fc & IEEE80211_FCTL_TODS) || !(fc & IEEE80211_FCTL_FROMDS))
+ retval -= ETH_ALEN;
+
+ return retval;
+}
+
+#endif /* __ipw2200_h__ */
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 9c2d07cde010..d7947358e49d 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -94,6 +94,8 @@
#include <net/iw_handler.h>
#include <net/ieee80211.h>
+#include <net/ieee80211.h>
+
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/system.h>
@@ -101,7 +103,6 @@
#include "hermes.h"
#include "hermes_rid.h"
#include "orinoco.h"
-#include "ieee802_11.h"
/********************************************************************/
/* Module information */
@@ -150,7 +151,7 @@ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2)
#define ORINOCO_MIN_MTU 256
-#define ORINOCO_MAX_MTU (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD)
+#define ORINOCO_MAX_MTU (IEEE80211_DATA_LEN - ENCAPS_OVERHEAD)
#define SYMBOL_MAX_VER_LEN (14)
#define USER_BAP 0
@@ -442,7 +443,7 @@ static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) )
return -EINVAL;
- if ( (new_mtu + ENCAPS_OVERHEAD + IEEE802_11_HLEN) >
+ if ( (new_mtu + ENCAPS_OVERHEAD + IEEE80211_HLEN) >
(priv->nicbuf_size - ETH_HLEN) )
return -EINVAL;
@@ -918,7 +919,7 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
data. */
return;
}
- if (length > IEEE802_11_DATA_LEN) {
+ if (length > IEEE80211_DATA_LEN) {
printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
dev->name, length);
stats->rx_length_errors++;
@@ -2272,7 +2273,7 @@ static int orinoco_init(struct net_device *dev)
/* No need to lock, the hw_unavailable flag is already set in
* alloc_orinocodev() */
- priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN;
+ priv->nicbuf_size = IEEE80211_FRAME_LEN + ETH_HLEN;
/* Initialize the firmware */
err = orinoco_reinit_firmware(dev);
diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c
index 6c42b573a95a..4b0acae22b0d 100644
--- a/drivers/net/wireless/strip.c
+++ b/drivers/net/wireless/strip.c
@@ -209,7 +209,7 @@ enum {
NoStructure = 0, /* Really old firmware */
StructuredMessages = 1, /* Parsable AT response msgs */
ChecksummedMessages = 2 /* Parsable AT response msgs with checksums */
-} FirmwareLevel;
+};
struct strip {
int magic;
diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c
index f6130a53b796..183c4732ef65 100644
--- a/drivers/net/wireless/wavelan_cs.c
+++ b/drivers/net/wireless/wavelan_cs.c
@@ -59,6 +59,12 @@
/* Do *NOT* add other headers here, you are guaranteed to be wrong - Jean II */
#include "wavelan_cs.p.h" /* Private header */
+#ifdef WAVELAN_ROAMING
+static void wl_cell_expiry(unsigned long data);
+static void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp);
+static void wv_nwid_filter(unsigned char mode, net_local *lp);
+#endif /* WAVELAN_ROAMING */
+
/************************* MISC SUBROUTINES **************************/
/*
* Subroutines which won't fit in one of the following category
@@ -500,9 +506,9 @@ fee_write(u_long base, /* i/o port of the card */
#ifdef WAVELAN_ROAMING /* Conditional compile, see wavelan_cs.h */
-unsigned char WAVELAN_BEACON_ADDRESS[]= {0x09,0x00,0x0e,0x20,0x03,0x00};
+static unsigned char WAVELAN_BEACON_ADDRESS[] = {0x09,0x00,0x0e,0x20,0x03,0x00};
-void wv_roam_init(struct net_device *dev)
+static void wv_roam_init(struct net_device *dev)
{
net_local *lp= netdev_priv(dev);
@@ -531,7 +537,7 @@ void wv_roam_init(struct net_device *dev)
printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name);
}
-void wv_roam_cleanup(struct net_device *dev)
+static void wv_roam_cleanup(struct net_device *dev)
{
wavepoint_history *ptr,*old_ptr;
net_local *lp= netdev_priv(dev);
@@ -550,7 +556,7 @@ void wv_roam_cleanup(struct net_device *dev)
}
/* Enable/Disable NWID promiscuous mode on a given device */
-void wv_nwid_filter(unsigned char mode, net_local *lp)
+static void wv_nwid_filter(unsigned char mode, net_local *lp)
{
mm_t m;
unsigned long flags;
@@ -575,7 +581,7 @@ void wv_nwid_filter(unsigned char mode, net_local *lp)
}
/* Find a record in the WavePoint table matching a given NWID */
-wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
+static wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
{
wavepoint_history *ptr=lp->wavepoint_table.head;
@@ -588,7 +594,7 @@ wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
}
/* Create a new wavepoint table entry */
-wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp)
+static wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp)
{
wavepoint_history *new_wavepoint;
@@ -624,7 +630,7 @@ wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_
}
/* Remove a wavepoint entry from WavePoint table */
-void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
+static void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
{
if(wavepoint==NULL)
return;
@@ -646,7 +652,7 @@ void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
}
/* Timer callback function - checks WavePoint table for stale entries */
-void wl_cell_expiry(unsigned long data)
+static void wl_cell_expiry(unsigned long data)
{
net_local *lp=(net_local *)data;
wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point;
@@ -686,7 +692,7 @@ void wl_cell_expiry(unsigned long data)
}
/* Update SNR history of a wavepoint */
-void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq)
+static void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq)
{
int i=0,num_missed=0,ptr=0;
int average_fast=0,average_slow=0;
@@ -723,7 +729,7 @@ void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsi
}
/* Perform a handover to a new WavePoint */
-void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
+static void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
{
kio_addr_t base = lp->dev->base_addr;
mm_t m;
diff --git a/drivers/net/wireless/wavelan_cs.h b/drivers/net/wireless/wavelan_cs.h
index 29cff6daf860..fabc63ee153c 100644
--- a/drivers/net/wireless/wavelan_cs.h
+++ b/drivers/net/wireless/wavelan_cs.h
@@ -62,7 +62,7 @@
* like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this
* part to accommodate your hardware...
*/
-const unsigned char MAC_ADDRESSES[][3] =
+static const unsigned char MAC_ADDRESSES[][3] =
{
{ 0x08, 0x00, 0x0E }, /* AT&T Wavelan (standard) & DEC RoamAbout */
{ 0x08, 0x00, 0x6A }, /* AT&T Wavelan (alternate) */
@@ -79,14 +79,14 @@ const unsigned char MAC_ADDRESSES[][3] =
* (as read in the offset register of the dac area).
* Used to map channel numbers used by `wfreqsel' to frequencies
*/
-const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
+static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
0xD0, 0xF0, 0xF8, 0x150 };
/* Frequencies of the 1.0 modem (fixed frequencies).
* Use to map the PSA `subband' to a frequency
* Note : all frequencies apart from the first one need to be multiplied by 10
*/
-const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
+static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
/*************************** PC INTERFACE ****************************/
diff --git a/drivers/net/wireless/wavelan_cs.p.h b/drivers/net/wireless/wavelan_cs.p.h
index 677ff71883cb..01d882be8790 100644
--- a/drivers/net/wireless/wavelan_cs.p.h
+++ b/drivers/net/wireless/wavelan_cs.p.h
@@ -647,23 +647,6 @@ struct net_local
void __iomem *mem;
};
-/**************************** PROTOTYPES ****************************/
-
-#ifdef WAVELAN_ROAMING
-/* ---------------------- ROAMING SUBROUTINES -----------------------*/
-
-wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp);
-wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local *lp);
-void wl_del_wavepoint(wavepoint_history *wavepoint, net_local *lp);
-void wl_cell_expiry(unsigned long data);
-wavepoint_history *wl_best_sigqual(int fast_search, net_local *lp);
-void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq);
-void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp);
-void wv_nwid_filter(unsigned char mode, net_local *lp);
-void wv_roam_init(struct net_device *dev);
-void wv_roam_cleanup(struct net_device *dev);
-#endif /* WAVELAN_ROAMING */
-
/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
static inline u_char /* data */
hasr_read(u_long); /* Read the host interface : base address */
diff --git a/drivers/net/wireless/wl3501.h b/drivers/net/wireless/wl3501.h
index 8636d9306785..b5719437e981 100644
--- a/drivers/net/wireless/wl3501.h
+++ b/drivers/net/wireless/wl3501.h
@@ -2,7 +2,7 @@
#define __WL3501_H__
#include <linux/spinlock.h>
-#include "ieee802_11.h"
+#include <net/ieee80211.h>
/* define for WLA 2.0 */
#define WL3501_BLKSZ 256
@@ -548,7 +548,7 @@ struct wl3501_80211_tx_plcp_hdr {
struct wl3501_80211_tx_hdr {
struct wl3501_80211_tx_plcp_hdr pclp_hdr;
- struct ieee802_11_hdr mac_hdr;
+ struct ieee80211_hdr mac_hdr;
} __attribute__ ((packed));
/*
diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c
index dd902126d018..7cc5edbf6ede 100644
--- a/drivers/net/wireless/wl3501_cs.c
+++ b/drivers/net/wireless/wl3501_cs.c
@@ -296,7 +296,8 @@ static int wl3501_get_flash_mac_addr(struct wl3501_card *this)
*
* Move 'size' bytes from PC to card. (Shouldn't be interrupted)
*/
-void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src, int size)
+static void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src,
+ int size)
{
/* switch to SRAM Page 0 */
wl3501_switch_page(this, (dest & 0x8000) ? WL3501_BSS_SPAGE1 :
@@ -317,8 +318,8 @@ void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src, int size)
*
* Move 'size' bytes from card to PC. (Shouldn't be interrupted)
*/
-void wl3501_get_from_wla(struct wl3501_card *this, u16 src, void *dest,
- int size)
+static void wl3501_get_from_wla(struct wl3501_card *this, u16 src, void *dest,
+ int size)
{
/* switch to SRAM Page 0 */
wl3501_switch_page(this, (src & 0x8000) ? WL3501_BSS_SPAGE1 :
@@ -1438,14 +1439,14 @@ fail:
goto out;
}
-struct net_device_stats *wl3501_get_stats(struct net_device *dev)
+static struct net_device_stats *wl3501_get_stats(struct net_device *dev)
{
struct wl3501_card *this = dev->priv;
return &this->stats;
}
-struct iw_statistics *wl3501_get_wireless_stats(struct net_device *dev)
+static struct iw_statistics *wl3501_get_wireless_stats(struct net_device *dev)
{
struct wl3501_card *this = dev->priv;
struct iw_statistics *wstats = &this->wstats;