Merge branch 'master' into upstream
diff --git a/Documentation/networking/README.ipw2200 b/Documentation/networking/README.ipw2200
index acb30c5..4f2a40f 100644
--- a/Documentation/networking/README.ipw2200
+++ b/Documentation/networking/README.ipw2200
@@ -14,8 +14,8 @@
README.ipw2200
-Version: 1.0.8
-Date : October 20, 2005
+Version: 1.1.2
+Date : March 30, 2006
Index
@@ -103,7 +103,7 @@
1.1. Overview of Features
-----------------------------------------------
-The current release (1.0.8) supports the following features:
+The current release (1.1.2) supports the following features:
+ BSS mode (Infrastructure, Managed)
+ IBSS mode (Ad-Hoc)
@@ -247,8 +247,8 @@
% cat /sys/bus/pci/drivers/ipw2200/debug_level
Will report the current debug level of the driver's logging subsystem
-(only available if CONFIG_IPW_DEBUG was configured when the driver was
-built).
+(only available if CONFIG_IPW2200_DEBUG was configured when the driver
+was built).
You can set the debug level via:
diff --git a/MAINTAINERS b/MAINTAINERS
index d6a8e5b..3d55010 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1404,6 +1404,8 @@
M: jesse.brandeburg@intel.com
P: Jeff Kirsher
M: jeffrey.t.kirsher@intel.com
+P: Auke Kok
+M: auke-jan.h.kok@intel.com
W: http://sourceforge.net/projects/e1000/
S: Supported
@@ -1416,6 +1418,8 @@
M: jesse.brandeburg@intel.com
P: Jeff Kirsher
M: jeffrey.t.kirsher@intel.com
+P: Auke Kok
+M: auke-jan.h.kok@intel.com
W: http://sourceforge.net/projects/e1000/
S: Supported
@@ -1428,6 +1432,8 @@
M: john.ronciak@intel.com
P: Jesse Brandeburg
M: jesse.brandeburg@intel.com
+P: Auke Kok
+M: auke-jan.h.kok@intel.com
W: http://sourceforge.net/projects/e1000/
S: Supported
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index bdaaad8..68bc073 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -865,6 +865,22 @@
<file:Documentation/networking/net-modules.txt>. The module will be
called dm9000.
+config SMC911X
+ tristate "SMSC LAN911[5678] support"
+ select CRC32
+ select MII
+ depends on NET_ETHERNET
+ help
+ This is a driver for SMSC's LAN911x series of Ethernet chipsets
+ including the new LAN9115, LAN9116, LAN9117, and LAN9118.
+ Say Y if you want it compiled into the kernel,
+ and read the Ethernet-HOWTO, available from
+ <http://www.linuxdoc.org/docs.html#howto>.
+
+ This driver is also available as a module. The module will be
+ called smc911x. If you want to compile it as a module, say M
+ here and read <file:Documentation/modules.txt>
+
config NET_VENDOR_RACAL
bool "Racal-Interlan (Micom) NI cards"
depends on NET_ETHERNET && ISA
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b90468a..b01cc9a 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -193,6 +193,7 @@
obj-$(CONFIG_IBMVETH) += ibmveth.o
obj-$(CONFIG_S2IO) += s2io.o
obj-$(CONFIG_SMC91X) += smc91x.o
+obj-$(CONFIG_SMC911X) += smc911x.o
obj-$(CONFIG_DM9000) += dm9000.o
obj-$(CONFIG_FEC_8XX) += fec_8xx/
diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c
index 1363083..d5dfc78 100644
--- a/drivers/net/au1000_eth.c
+++ b/drivers/net/au1000_eth.c
@@ -2,7 +2,7 @@
*
* Alchemy Au1x00 ethernet driver
*
- * Copyright 2001,2002,2003 MontaVista Software Inc.
+ * Copyright 2001-2003, 2006 MontaVista Software Inc.
* Copyright 2002 TimeSys Corp.
* Added ethtool/mii-tool support,
* Copyright 2004 Matt Porter <mporter@kernel.crashing.org>
@@ -67,7 +67,7 @@
static int au1000_debug = 3;
#endif
-#define DRV_NAME "au1000eth"
+#define DRV_NAME "au1000_eth"
#define DRV_VERSION "1.5"
#define DRV_AUTHOR "Pete Popov <ppopov@embeddedalley.com>"
#define DRV_DESC "Au1xxx on-chip Ethernet driver"
@@ -79,7 +79,7 @@
// prototypes
static void hard_stop(struct net_device *);
static void enable_rx_tx(struct net_device *dev);
-static struct net_device * au1000_probe(u32 ioaddr, int irq, int port_num);
+static struct net_device * au1000_probe(int port_num);
static int au1000_init(struct net_device *);
static int au1000_open(struct net_device *);
static int au1000_close(struct net_device *);
@@ -1159,12 +1159,27 @@
}
static struct {
- int port;
u32 base_addr;
u32 macen_addr;
int irq;
struct net_device *dev;
-} iflist[2];
+} iflist[2] = {
+#ifdef CONFIG_SOC_AU1000
+ {AU1000_ETH0_BASE, AU1000_MAC0_ENABLE, AU1000_MAC0_DMA_INT},
+ {AU1000_ETH1_BASE, AU1000_MAC1_ENABLE, AU1000_MAC1_DMA_INT}
+#endif
+#ifdef CONFIG_SOC_AU1100
+ {AU1100_ETH0_BASE, AU1100_MAC0_ENABLE, AU1100_MAC0_DMA_INT}
+#endif
+#ifdef CONFIG_SOC_AU1500
+ {AU1500_ETH0_BASE, AU1500_MAC0_ENABLE, AU1500_MAC0_DMA_INT},
+ {AU1500_ETH1_BASE, AU1500_MAC1_ENABLE, AU1500_MAC1_DMA_INT}
+#endif
+#ifdef CONFIG_SOC_AU1550
+ {AU1550_ETH0_BASE, AU1550_MAC0_ENABLE, AU1550_MAC0_DMA_INT},
+ {AU1550_ETH1_BASE, AU1550_MAC1_ENABLE, AU1550_MAC1_DMA_INT}
+#endif
+};
static int num_ifs;
@@ -1175,58 +1190,14 @@
*/
static int __init au1000_init_module(void)
{
- struct cpuinfo_mips *c = ¤t_cpu_data;
int ni = (int)((au_readl(SYS_PINFUNC) & (u32)(SYS_PF_NI2)) >> 4);
struct net_device *dev;
int i, found_one = 0;
- switch (c->cputype) {
-#ifdef CONFIG_SOC_AU1000
- case CPU_AU1000:
- num_ifs = 2 - ni;
- iflist[0].base_addr = AU1000_ETH0_BASE;
- iflist[1].base_addr = AU1000_ETH1_BASE;
- iflist[0].macen_addr = AU1000_MAC0_ENABLE;
- iflist[1].macen_addr = AU1000_MAC1_ENABLE;
- iflist[0].irq = AU1000_MAC0_DMA_INT;
- iflist[1].irq = AU1000_MAC1_DMA_INT;
- break;
-#endif
-#ifdef CONFIG_SOC_AU1100
- case CPU_AU1100:
- num_ifs = 1 - ni;
- iflist[0].base_addr = AU1100_ETH0_BASE;
- iflist[0].macen_addr = AU1100_MAC0_ENABLE;
- iflist[0].irq = AU1100_MAC0_DMA_INT;
- break;
-#endif
-#ifdef CONFIG_SOC_AU1500
- case CPU_AU1500:
- num_ifs = 2 - ni;
- iflist[0].base_addr = AU1500_ETH0_BASE;
- iflist[1].base_addr = AU1500_ETH1_BASE;
- iflist[0].macen_addr = AU1500_MAC0_ENABLE;
- iflist[1].macen_addr = AU1500_MAC1_ENABLE;
- iflist[0].irq = AU1500_MAC0_DMA_INT;
- iflist[1].irq = AU1500_MAC1_DMA_INT;
- break;
-#endif
-#ifdef CONFIG_SOC_AU1550
- case CPU_AU1550:
- num_ifs = 2 - ni;
- iflist[0].base_addr = AU1550_ETH0_BASE;
- iflist[1].base_addr = AU1550_ETH1_BASE;
- iflist[0].macen_addr = AU1550_MAC0_ENABLE;
- iflist[1].macen_addr = AU1550_MAC1_ENABLE;
- iflist[0].irq = AU1550_MAC0_DMA_INT;
- iflist[1].irq = AU1550_MAC1_DMA_INT;
- break;
-#endif
- default:
- num_ifs = 0;
- }
+ num_ifs = NUM_ETH_INTERFACES - ni;
+
for(i = 0; i < num_ifs; i++) {
- dev = au1000_probe(iflist[i].base_addr, iflist[i].irq, i);
+ dev = au1000_probe(i);
iflist[i].dev = dev;
if (dev)
found_one++;
@@ -1435,8 +1406,7 @@
.get_link = au1000_get_link
};
-static struct net_device *
-au1000_probe(u32 ioaddr, int irq, int port_num)
+static struct net_device * au1000_probe(int port_num)
{
static unsigned version_printed = 0;
struct au1000_private *aup = NULL;
@@ -1444,94 +1414,95 @@
db_dest_t *pDB, *pDBfree;
char *pmac, *argptr;
char ethaddr[6];
- int i, err;
+ int irq, i, err;
+ u32 base, macen;
- if (!request_mem_region(CPHYSADDR(ioaddr), MAC_IOSIZE, "Au1x00 ENET"))
+ if (port_num >= NUM_ETH_INTERFACES)
+ return NULL;
+
+ base = CPHYSADDR(iflist[port_num].base_addr );
+ macen = CPHYSADDR(iflist[port_num].macen_addr);
+ irq = iflist[port_num].irq;
+
+ if (!request_mem_region( base, MAC_IOSIZE, "Au1x00 ENET") ||
+ !request_mem_region(macen, 4, "Au1x00 ENET"))
return NULL;
- if (version_printed++ == 0)
+ if (version_printed++ == 0)
printk("%s version %s %s\n", DRV_NAME, DRV_VERSION, DRV_AUTHOR);
dev = alloc_etherdev(sizeof(struct au1000_private));
if (!dev) {
- printk (KERN_ERR "au1000 eth: alloc_etherdev failed\n");
+ printk(KERN_ERR "%s: alloc_etherdev failed\n", DRV_NAME);
return NULL;
}
- if ((err = register_netdev(dev))) {
- printk(KERN_ERR "Au1x_eth Cannot register net device err %d\n",
- err);
+ if ((err = register_netdev(dev)) != 0) {
+ printk(KERN_ERR "%s: Cannot register net device, error %d\n",
+ DRV_NAME, err);
free_netdev(dev);
return NULL;
}
- printk("%s: Au1x Ethernet found at 0x%x, irq %d\n",
- dev->name, ioaddr, irq);
+ printk("%s: Au1xx0 Ethernet found at 0x%x, irq %d\n",
+ dev->name, base, irq);
aup = dev->priv;
/* Allocate the data buffers */
/* Snooping works fine with eth on all au1xxx */
- aup->vaddr = (u32)dma_alloc_noncoherent(NULL,
- MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS),
- &aup->dma_addr,
- 0);
+ aup->vaddr = (u32)dma_alloc_noncoherent(NULL, MAX_BUF_SIZE *
+ (NUM_TX_BUFFS + NUM_RX_BUFFS),
+ &aup->dma_addr, 0);
if (!aup->vaddr) {
free_netdev(dev);
- release_mem_region(CPHYSADDR(ioaddr), MAC_IOSIZE);
+ release_mem_region( base, MAC_IOSIZE);
+ release_mem_region(macen, 4);
return NULL;
}
/* aup->mac is the base address of the MAC's registers */
- aup->mac = (volatile mac_reg_t *)((unsigned long)ioaddr);
+ aup->mac = (volatile mac_reg_t *)iflist[port_num].base_addr;
+
/* Setup some variables for quick register address access */
- if (ioaddr == iflist[0].base_addr)
- {
- /* check env variables first */
- if (!get_ethernet_addr(ethaddr)) {
+ aup->enable = (volatile u32 *)iflist[port_num].macen_addr;
+ aup->mac_id = port_num;
+ au_macs[port_num] = aup;
+
+ if (port_num == 0) {
+ /* Check the environment variables first */
+ if (get_ethernet_addr(ethaddr) == 0)
memcpy(au1000_mac_addr, ethaddr, sizeof(au1000_mac_addr));
- } else {
+ else {
/* Check command line */
argptr = prom_getcmdline();
- if ((pmac = strstr(argptr, "ethaddr=")) == NULL) {
- printk(KERN_INFO "%s: No mac address found\n",
- dev->name);
- /* use the hard coded mac addresses */
- } else {
+ if ((pmac = strstr(argptr, "ethaddr=")) == NULL)
+ printk(KERN_INFO "%s: No MAC address found\n",
+ dev->name);
+ /* Use the hard coded MAC addresses */
+ else {
str2eaddr(ethaddr, pmac + strlen("ethaddr="));
memcpy(au1000_mac_addr, ethaddr,
- sizeof(au1000_mac_addr));
+ sizeof(au1000_mac_addr));
}
}
- aup->enable = (volatile u32 *)
- ((unsigned long)iflist[0].macen_addr);
- memcpy(dev->dev_addr, au1000_mac_addr, sizeof(au1000_mac_addr));
- setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR);
- aup->mac_id = 0;
- au_macs[0] = aup;
- }
- else
- if (ioaddr == iflist[1].base_addr)
- {
- aup->enable = (volatile u32 *)
- ((unsigned long)iflist[1].macen_addr);
- memcpy(dev->dev_addr, au1000_mac_addr, sizeof(au1000_mac_addr));
- dev->dev_addr[4] += 0x10;
- setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR);
- aup->mac_id = 1;
- au_macs[1] = aup;
- }
- else
- {
- printk(KERN_ERR "%s: bad ioaddr\n", dev->name);
- }
- /* bring the device out of reset, otherwise probing the mii
- * will hang */
+ setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR);
+ } else if (port_num == 1)
+ setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR);
+
+ /*
+ * Assign to the Ethernet ports two consecutive MAC addresses
+ * to match those that are printed on their stickers
+ */
+ memcpy(dev->dev_addr, au1000_mac_addr, sizeof(au1000_mac_addr));
+ dev->dev_addr[5] += port_num;
+
+ /* Bring the device out of reset, otherwise probing the MII will hang */
*aup->enable = MAC_EN_CLOCK_ENABLE;
au_sync_delay(2);
- *aup->enable = MAC_EN_RESET0 | MAC_EN_RESET1 |
- MAC_EN_RESET2 | MAC_EN_CLOCK_ENABLE;
+ *aup->enable = MAC_EN_RESET0 | MAC_EN_RESET1 | MAC_EN_RESET2 |
+ MAC_EN_CLOCK_ENABLE;
au_sync_delay(2);
aup->mii = kmalloc(sizeof(struct mii_phy), GFP_KERNEL);
@@ -1580,7 +1551,7 @@
}
spin_lock_init(&aup->lock);
- dev->base_addr = ioaddr;
+ dev->base_addr = base;
dev->irq = irq;
dev->open = au1000_open;
dev->hard_start_xmit = au1000_tx;
@@ -1614,13 +1585,12 @@
if (aup->tx_db_inuse[i])
ReleaseDB(aup, aup->tx_db_inuse[i]);
}
- dma_free_noncoherent(NULL,
- MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS),
- (void *)aup->vaddr,
- aup->dma_addr);
+ dma_free_noncoherent(NULL, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
+ (void *)aup->vaddr, aup->dma_addr);
unregister_netdev(dev);
free_netdev(dev);
- release_mem_region(CPHYSADDR(ioaddr), MAC_IOSIZE);
+ release_mem_region( base, MAC_IOSIZE);
+ release_mem_region(macen, 4);
return NULL;
}
@@ -1805,20 +1775,18 @@
aup = (struct au1000_private *) dev->priv;
unregister_netdev(dev);
kfree(aup->mii);
- for (j = 0; j < NUM_RX_DMA; j++) {
+ for (j = 0; j < NUM_RX_DMA; j++)
if (aup->rx_db_inuse[j])
ReleaseDB(aup, aup->rx_db_inuse[j]);
- }
- for (j = 0; j < NUM_TX_DMA; j++) {
+ for (j = 0; j < NUM_TX_DMA; j++)
if (aup->tx_db_inuse[j])
ReleaseDB(aup, aup->tx_db_inuse[j]);
- }
- dma_free_noncoherent(NULL,
- MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS),
- (void *)aup->vaddr,
- aup->dma_addr);
+ dma_free_noncoherent(NULL, MAX_BUF_SIZE *
+ (NUM_TX_BUFFS + NUM_RX_BUFFS),
+ (void *)aup->vaddr, aup->dma_addr);
+ release_mem_region(dev->base_addr, MAC_IOSIZE);
+ release_mem_region(CPHYSADDR(iflist[i].macen_addr), 4);
free_netdev(dev);
- release_mem_region(CPHYSADDR(iflist[i].base_addr), MAC_IOSIZE);
}
}
}
diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c
index ac48f75..39f36aa 100644
--- a/drivers/net/cassini.c
+++ b/drivers/net/cassini.c
@@ -4877,7 +4877,7 @@
const struct pci_device_id *ent)
{
static int cas_version_printed = 0;
- unsigned long casreg_base, casreg_len;
+ unsigned long casreg_len;
struct net_device *dev;
struct cas *cp;
int i, err, pci_using_dac;
@@ -4972,7 +4972,6 @@
pci_using_dac = 0;
}
- casreg_base = pci_resource_start(pdev, 0);
casreg_len = pci_resource_len(pdev, 0);
cp = netdev_priv(dev);
@@ -5024,7 +5023,7 @@
cp->timer_ticks = 0;
/* give us access to cassini registers */
- cp->regs = ioremap(casreg_base, casreg_len);
+ cp->regs = pci_iomap(pdev, 0, casreg_len);
if (cp->regs == 0UL) {
printk(KERN_ERR PFX "Cannot map device registers, "
"aborting.\n");
@@ -5123,7 +5122,7 @@
cas_shutdown(cp);
mutex_unlock(&cp->pm_mutex);
- iounmap(cp->regs);
+ pci_iounmap(pdev, cp->regs);
err_out_free_res:
@@ -5171,7 +5170,7 @@
#endif
pci_free_consistent(pdev, sizeof(struct cas_init_block),
cp->init_block, cp->block_dvma);
- iounmap(cp->regs);
+ pci_iounmap(pdev, cp->regs);
free_netdev(dev);
pci_release_regions(pdev);
pci_disable_device(pdev);
diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile
index ca9f895..92823ac 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/e1000/Makefile
@@ -22,6 +22,7 @@
#
# Contact Information:
# Linux NICS <linux.nics@intel.com>
+# e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
# Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
#
################################################################################
diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h
index 281de41..2bc34fb 100644
--- a/drivers/net/e1000/e1000.h
+++ b/drivers/net/e1000/e1000.h
@@ -1,7 +1,7 @@
/*******************************************************************************
- Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+ Copyright(c) 1999 - 2006 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
@@ -22,6 +22,7 @@
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
@@ -114,6 +115,8 @@
/* Supported Rx Buffer Sizes */
#define E1000_RXBUFFER_128 128 /* Used for packet split */
#define E1000_RXBUFFER_256 256 /* Used for packet split */
+#define E1000_RXBUFFER_512 512
+#define E1000_RXBUFFER_1024 1024
#define E1000_RXBUFFER_2048 2048
#define E1000_RXBUFFER_4096 4096
#define E1000_RXBUFFER_8192 8192
@@ -334,7 +337,6 @@
boolean_t have_msi;
#endif
/* to not mess up cache alignment, always add to the bottom */
- boolean_t txb2b;
#ifdef NETIF_F_TSO
boolean_t tso_force;
#endif
diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c
index ecccca3..e48dc57 100644
--- a/drivers/net/e1000/e1000_ethtool.c
+++ b/drivers/net/e1000/e1000_ethtool.c
@@ -1,7 +1,7 @@
/*******************************************************************************
- Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+ Copyright(c) 1999 - 2006 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
@@ -22,6 +22,7 @@
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c
index 523c2c9..4c796e5 100644
--- a/drivers/net/e1000/e1000_hw.c
+++ b/drivers/net/e1000/e1000_hw.c
@@ -1,7 +1,7 @@
/*******************************************************************************
- Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+ Copyright(c) 1999 - 2006 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
@@ -22,6 +22,7 @@
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h
index 150e45e..03d07eb 100644
--- a/drivers/net/e1000/e1000_hw.h
+++ b/drivers/net/e1000/e1000_hw.h
@@ -1,7 +1,7 @@
/*******************************************************************************
- Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+ Copyright(c) 1999 - 2006 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
@@ -22,6 +22,7 @@
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index c99e878..fb8cef6 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -1,7 +1,7 @@
/*******************************************************************************
- Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+ Copyright(c) 1999 - 2006 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
@@ -22,6 +22,7 @@
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
@@ -74,9 +75,9 @@
#else
#define DRIVERNAPI "-NAPI"
#endif
-#define DRV_VERSION "7.0.33-k2"DRIVERNAPI
+#define DRV_VERSION "7.0.38-k2"DRIVERNAPI
char e1000_driver_version[] = DRV_VERSION;
-static char e1000_copyright[] = "Copyright (c) 1999-2005 Intel Corporation.";
+static char e1000_copyright[] = "Copyright (c) 1999-2006 Intel Corporation.";
/* e1000_pci_tbl - PCI Device ID Table
*
@@ -208,8 +209,8 @@
static void e1000_tx_timeout(struct net_device *dev);
static void e1000_reset_task(struct net_device *dev);
static void e1000_smartspeed(struct e1000_adapter *adapter);
-static inline int e1000_82547_fifo_workaround(struct e1000_adapter *adapter,
- struct sk_buff *skb);
+static int e1000_82547_fifo_workaround(struct e1000_adapter *adapter,
+ struct sk_buff *skb);
static void e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp);
static void e1000_vlan_rx_add_vid(struct net_device *netdev, uint16_t vid);
@@ -291,7 +292,7 @@
* @adapter: board private structure
**/
-static inline void
+static void
e1000_irq_disable(struct e1000_adapter *adapter)
{
atomic_inc(&adapter->irq_sem);
@@ -305,7 +306,7 @@
* @adapter: board private structure
**/
-static inline void
+static void
e1000_irq_enable(struct e1000_adapter *adapter)
{
if (likely(atomic_dec_and_test(&adapter->irq_sem))) {
@@ -349,7 +350,7 @@
*
**/
-static inline void
+static void
e1000_release_hw_control(struct e1000_adapter *adapter)
{
uint32_t ctrl_ext;
@@ -359,6 +360,7 @@
switch (adapter->hw.mac_type) {
case e1000_82571:
case e1000_82572:
+ case e1000_80003es2lan:
ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
E1000_WRITE_REG(&adapter->hw, CTRL_EXT,
ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD);
@@ -383,7 +385,7 @@
*
**/
-static inline void
+static void
e1000_get_hw_control(struct e1000_adapter *adapter)
{
uint32_t ctrl_ext;
@@ -392,6 +394,7 @@
switch (adapter->hw.mac_type) {
case e1000_82571:
case e1000_82572:
+ case e1000_80003es2lan:
ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
E1000_WRITE_REG(&adapter->hw, CTRL_EXT,
ctrl_ext | E1000_CTRL_EXT_DRV_LOAD);
@@ -419,7 +422,7 @@
uint16_t mii_reg;
e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &mii_reg);
if (mii_reg & MII_CR_POWER_DOWN)
- e1000_phy_reset(&adapter->hw);
+ e1000_phy_hw_reset(&adapter->hw);
}
e1000_set_multi(netdev);
@@ -970,8 +973,8 @@
pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word);
- adapter->rx_buffer_len = E1000_RXBUFFER_2048;
- adapter->rx_ps_bsize0 = E1000_RXBUFFER_256;
+ adapter->rx_buffer_len = MAXIMUM_ETHERNET_FRAME_SIZE;
+ adapter->rx_ps_bsize0 = E1000_RXBUFFER_128;
hw->max_frame_size = netdev->mtu +
ENET_HEADER_SIZE + ETHERNET_FCS_SIZE;
hw->min_frame_size = MINIMUM_ETHERNET_FRAME_SIZE;
@@ -1179,7 +1182,7 @@
* @start: address of beginning of memory
* @len: length of memory
**/
-static inline boolean_t
+static boolean_t
e1000_check_64k_bound(struct e1000_adapter *adapter,
void *start, unsigned long len)
{
@@ -1597,14 +1600,21 @@
rctl |= E1000_RCTL_LPE;
/* Setup buffer sizes */
- if (adapter->hw.mac_type >= e1000_82571) {
- /* We can now specify buffers in 1K increments.
- * BSIZE and BSEX are ignored in this case. */
- rctl |= adapter->rx_buffer_len << 0x11;
- } else {
- rctl &= ~E1000_RCTL_SZ_4096;
- rctl |= E1000_RCTL_BSEX;
- switch (adapter->rx_buffer_len) {
+ rctl &= ~E1000_RCTL_SZ_4096;
+ rctl |= E1000_RCTL_BSEX;
+ switch (adapter->rx_buffer_len) {
+ case E1000_RXBUFFER_256:
+ rctl |= E1000_RCTL_SZ_256;
+ rctl &= ~E1000_RCTL_BSEX;
+ break;
+ case E1000_RXBUFFER_512:
+ rctl |= E1000_RCTL_SZ_512;
+ rctl &= ~E1000_RCTL_BSEX;
+ break;
+ case E1000_RXBUFFER_1024:
+ rctl |= E1000_RCTL_SZ_1024;
+ rctl &= ~E1000_RCTL_BSEX;
+ break;
case E1000_RXBUFFER_2048:
default:
rctl |= E1000_RCTL_SZ_2048;
@@ -1619,7 +1629,6 @@
case E1000_RXBUFFER_16384:
rctl |= E1000_RCTL_SZ_16384;
break;
- }
}
#ifndef CONFIG_E1000_DISABLE_PACKET_SPLIT
@@ -1713,7 +1722,7 @@
if (hw->mac_type >= e1000_82571) {
ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
/* Reset delay timers after every interrupt */
- ctrl_ext |= E1000_CTRL_EXT_CANC;
+ ctrl_ext |= E1000_CTRL_EXT_INT_TIMER_CLR;
#ifdef CONFIG_E1000_NAPI
/* Auto-Mask interrupts upon ICR read. */
ctrl_ext |= E1000_CTRL_EXT_IAME;
@@ -1805,7 +1814,7 @@
e1000_free_tx_resources(adapter, &adapter->tx_ring[i]);
}
-static inline void
+static void
e1000_unmap_and_free_tx_resource(struct e1000_adapter *adapter,
struct e1000_buffer *buffer_info)
{
@@ -2245,6 +2254,7 @@
if (link) {
if (!netif_carrier_ok(netdev)) {
+ boolean_t txb2b = 1;
e1000_get_speed_and_duplex(&adapter->hw,
&adapter->link_speed,
&adapter->link_duplex);
@@ -2258,23 +2268,22 @@
* and adjust the timeout factor */
netdev->tx_queue_len = adapter->tx_queue_len;
adapter->tx_timeout_factor = 1;
- adapter->txb2b = 1;
switch (adapter->link_speed) {
case SPEED_10:
- adapter->txb2b = 0;
+ txb2b = 0;
netdev->tx_queue_len = 10;
adapter->tx_timeout_factor = 8;
break;
case SPEED_100:
- adapter->txb2b = 0;
+ txb2b = 0;
netdev->tx_queue_len = 100;
/* maybe add some timeout factor ? */
break;
}
- if ((adapter->hw.mac_type == e1000_82571 ||
+ if ((adapter->hw.mac_type == e1000_82571 ||
adapter->hw.mac_type == e1000_82572) &&
- adapter->txb2b == 0) {
+ txb2b == 0) {
#define SPEED_MODE_BIT (1 << 21)
uint32_t tarc0;
tarc0 = E1000_READ_REG(&adapter->hw, TARC0);
@@ -2398,7 +2407,7 @@
#define E1000_TX_FLAGS_VLAN_MASK 0xffff0000
#define E1000_TX_FLAGS_VLAN_SHIFT 16
-static inline int
+static int
e1000_tso(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring,
struct sk_buff *skb)
{
@@ -2478,7 +2487,7 @@
return FALSE;
}
-static inline boolean_t
+static boolean_t
e1000_tx_csum(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring,
struct sk_buff *skb)
{
@@ -2514,7 +2523,7 @@
#define E1000_MAX_TXD_PWR 12
#define E1000_MAX_DATA_PER_TXD (1<<E1000_MAX_TXD_PWR)
-static inline int
+static int
e1000_tx_map(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring,
struct sk_buff *skb, unsigned int first, unsigned int max_per_txd,
unsigned int nr_frags, unsigned int mss)
@@ -2623,7 +2632,7 @@
return count;
}
-static inline void
+static void
e1000_tx_queue(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring,
int tx_flags, int count)
{
@@ -2687,7 +2696,7 @@
#define E1000_FIFO_HDR 0x10
#define E1000_82547_PAD_LEN 0x3E0
-static inline int
+static int
e1000_82547_fifo_workaround(struct e1000_adapter *adapter, struct sk_buff *skb)
{
uint32_t fifo_space = adapter->tx_fifo_size - adapter->tx_fifo_head;
@@ -2714,7 +2723,7 @@
}
#define MINIMUM_DHCP_PACKET_SIZE 282
-static inline int
+static int
e1000_transfer_dhcp_info(struct e1000_adapter *adapter, struct sk_buff *skb)
{
struct e1000_hw *hw = &adapter->hw;
@@ -2980,8 +2989,7 @@
/* Adapter-specific max frame size limits. */
switch (adapter->hw.mac_type) {
- case e1000_82542_rev2_0:
- case e1000_82542_rev2_1:
+ case e1000_undefined ... e1000_82542_rev2_1:
if (max_frame > MAXIMUM_ETHERNET_FRAME_SIZE) {
DPRINTK(PROBE, ERR, "Jumbo Frames not supported.\n");
return -EINVAL;
@@ -3015,27 +3023,32 @@
break;
}
+ /* NOTE: dev_alloc_skb reserves 16 bytes, and typically NET_IP_ALIGN
+ * means we reserve 2 more, this pushes us to allocate from the next
+ * larger slab size
+ * i.e. RXBUFFER_2048 --> size-4096 slab */
- if (adapter->hw.mac_type > e1000_82547_rev_2) {
- adapter->rx_buffer_len = max_frame;
- E1000_ROUNDUP(adapter->rx_buffer_len, 1024);
- } else {
- if(unlikely((adapter->hw.mac_type < e1000_82543) &&
- (max_frame > MAXIMUM_ETHERNET_FRAME_SIZE))) {
- DPRINTK(PROBE, ERR, "Jumbo Frames not supported "
- "on 82542\n");
- return -EINVAL;
- } else {
- if(max_frame <= E1000_RXBUFFER_2048)
- adapter->rx_buffer_len = E1000_RXBUFFER_2048;
- else if(max_frame <= E1000_RXBUFFER_4096)
- adapter->rx_buffer_len = E1000_RXBUFFER_4096;
- else if(max_frame <= E1000_RXBUFFER_8192)
- adapter->rx_buffer_len = E1000_RXBUFFER_8192;
- else if(max_frame <= E1000_RXBUFFER_16384)
- adapter->rx_buffer_len = E1000_RXBUFFER_16384;
- }
- }
+ if (max_frame <= E1000_RXBUFFER_256)
+ adapter->rx_buffer_len = E1000_RXBUFFER_256;
+ else if (max_frame <= E1000_RXBUFFER_512)
+ adapter->rx_buffer_len = E1000_RXBUFFER_512;
+ else if (max_frame <= E1000_RXBUFFER_1024)
+ adapter->rx_buffer_len = E1000_RXBUFFER_1024;
+ else if (max_frame <= E1000_RXBUFFER_2048)
+ adapter->rx_buffer_len = E1000_RXBUFFER_2048;
+ else if (max_frame <= E1000_RXBUFFER_4096)
+ adapter->rx_buffer_len = E1000_RXBUFFER_4096;
+ else if (max_frame <= E1000_RXBUFFER_8192)
+ adapter->rx_buffer_len = E1000_RXBUFFER_8192;
+ else if (max_frame <= E1000_RXBUFFER_16384)
+ adapter->rx_buffer_len = E1000_RXBUFFER_16384;
+
+ /* adjust allocation if LPE protects us, and we aren't using SBP */
+#define MAXIMUM_ETHERNET_VLAN_SIZE 1522
+ if (!adapter->hw.tbi_compatibility_on &&
+ ((max_frame == MAXIMUM_ETHERNET_FRAME_SIZE) ||
+ (max_frame == MAXIMUM_ETHERNET_VLAN_SIZE)))
+ adapter->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE;
netdev->mtu = new_mtu;
@@ -3163,7 +3176,6 @@
adapter->stats.crcerrs + adapter->stats.algnerrc +
adapter->stats.ruc + adapter->stats.roc +
adapter->stats.cexterr;
- adapter->net_stats.rx_dropped = 0;
adapter->net_stats.rx_length_errors = adapter->stats.ruc +
adapter->stats.roc;
adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs;
@@ -3389,13 +3401,15 @@
tx_ring->next_to_clean = i;
- spin_lock(&tx_ring->tx_lock);
-
+#define TX_WAKE_THRESHOLD 32
if (unlikely(cleaned && netif_queue_stopped(netdev) &&
- netif_carrier_ok(netdev)))
- netif_wake_queue(netdev);
-
- spin_unlock(&tx_ring->tx_lock);
+ netif_carrier_ok(netdev))) {
+ spin_lock(&tx_ring->tx_lock);
+ if (netif_queue_stopped(netdev) &&
+ (E1000_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))
+ netif_wake_queue(netdev);
+ spin_unlock(&tx_ring->tx_lock);
+ }
if (adapter->detect_tx_hung) {
/* Detect a transmit hang in hardware, this serializes the
@@ -3443,7 +3457,7 @@
* @sk_buff: socket buffer with received data
**/
-static inline void
+static void
e1000_rx_checksum(struct e1000_adapter *adapter,
uint32_t status_err, uint32_t csum,
struct sk_buff *skb)
@@ -3567,7 +3581,8 @@
flags);
length--;
} else {
- dev_kfree_skb_irq(skb);
+ /* recycle */
+ buffer_info->skb = skb;
goto next_desc;
}
}
@@ -3675,6 +3690,7 @@
i = rx_ring->next_to_clean;
rx_desc = E1000_RX_DESC_PS(*rx_ring, i);
staterr = le32_to_cpu(rx_desc->wb.middle.status_error);
+ buffer_info = &rx_ring->buffer_info[i];
while (staterr & E1000_RXD_STAT_DD) {
buffer_info = &rx_ring->buffer_info[i];
@@ -3735,7 +3751,7 @@
/* page alloc/put takes too long and effects small packet
* throughput, so unsplit small packets and save the alloc/put*/
- if (l1 && ((length + l1) < E1000_CB_LENGTH)) {
+ if (l1 && ((length + l1) <= adapter->rx_ps_bsize0)) {
u8 *vaddr;
/* there is no documentation about how to call
* kmap_atomic, so we can't hold the mapping
@@ -4157,7 +4173,7 @@
spin_unlock_irqrestore(&adapter->stats_lock, flags);
return -EIO;
}
- if (adapter->hw.phy_type == e1000_media_type_copper) {
+ if (adapter->hw.media_type == e1000_media_type_copper) {
switch (data->reg_num) {
case PHY_CTRL:
if (mii_reg & MII_CR_POWER_DOWN)
@@ -4516,21 +4532,13 @@
E1000_WRITE_REG(&adapter->hw, WUC, E1000_WUC_PME_EN);
E1000_WRITE_REG(&adapter->hw, WUFC, wufc);
- retval = pci_enable_wake(pdev, PCI_D3hot, 1);
- if (retval)
- DPRINTK(PROBE, ERR, "Error enabling D3 wake\n");
- retval = pci_enable_wake(pdev, PCI_D3cold, 1);
- if (retval)
- DPRINTK(PROBE, ERR, "Error enabling D3 cold wake\n");
+ pci_enable_wake(pdev, PCI_D3hot, 1);
+ pci_enable_wake(pdev, PCI_D3cold, 1);
} else {
E1000_WRITE_REG(&adapter->hw, WUC, 0);
E1000_WRITE_REG(&adapter->hw, WUFC, 0);
- retval = pci_enable_wake(pdev, PCI_D3hot, 0);
- if (retval)
- DPRINTK(PROBE, ERR, "Error enabling D3 wake\n");
- retval = pci_enable_wake(pdev, PCI_D3cold, 0);
- if (retval)
- DPRINTK(PROBE, ERR, "Error enabling D3 cold wake\n");
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
}
if (adapter->hw.mac_type >= e1000_82540 &&
@@ -4539,13 +4547,8 @@
if (manc & E1000_MANC_SMBUS_EN) {
manc |= E1000_MANC_ARP_EN;
E1000_WRITE_REG(&adapter->hw, MANC, manc);
- retval = pci_enable_wake(pdev, PCI_D3hot, 1);
- if (retval)
- DPRINTK(PROBE, ERR, "Error enabling D3 wake\n");
- retval = pci_enable_wake(pdev, PCI_D3cold, 1);
- if (retval)
- DPRINTK(PROBE, ERR,
- "Error enabling D3 cold wake\n");
+ pci_enable_wake(pdev, PCI_D3hot, 1);
+ pci_enable_wake(pdev, PCI_D3cold, 1);
}
}
@@ -4555,9 +4558,7 @@
pci_disable_device(pdev);
- retval = pci_set_power_state(pdev, pci_choose_state(pdev, state));
- if (retval)
- DPRINTK(PROBE, ERR, "Error in setting power state\n");
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0;
}
@@ -4568,22 +4569,15 @@
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct e1000_adapter *adapter = netdev_priv(netdev);
- int retval;
uint32_t manc, ret_val;
- retval = pci_set_power_state(pdev, PCI_D0);
- if (retval)
- DPRINTK(PROBE, ERR, "Error in setting power state\n");
+ pci_set_power_state(pdev, PCI_D0);
e1000_pci_restore_state(adapter);
ret_val = pci_enable_device(pdev);
pci_set_master(pdev);
- retval = pci_enable_wake(pdev, PCI_D3hot, 0);
- if (retval)
- DPRINTK(PROBE, ERR, "Error enabling D3 wake\n");
- retval = pci_enable_wake(pdev, PCI_D3cold, 0);
- if (retval)
- DPRINTK(PROBE, ERR, "Error enabling D3 cold wake\n");
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
e1000_reset(adapter);
E1000_WRITE_REG(&adapter->hw, WUS, ~0);
diff --git a/drivers/net/e1000/e1000_osdep.h b/drivers/net/e1000/e1000_osdep.h
index 9790db9..048d052 100644
--- a/drivers/net/e1000/e1000_osdep.h
+++ b/drivers/net/e1000/e1000_osdep.h
@@ -1,7 +1,7 @@
/*******************************************************************************
- Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+ Copyright(c) 1999 - 2006 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
@@ -22,6 +22,7 @@
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c
index e0a4d37..e55f896 100644
--- a/drivers/net/e1000/e1000_param.c
+++ b/drivers/net/e1000/e1000_param.c
@@ -1,7 +1,7 @@
/*******************************************************************************
- Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+ Copyright(c) 1999 - 2006 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
@@ -22,6 +22,7 @@
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
index b82191d..3b3e104 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/sis900.c
@@ -1,6 +1,6 @@
/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
Copyright 1999 Silicon Integrated System Corporation
- Revision: 1.08.09 Sep. 19 2005
+ Revision: 1.08.10 Apr. 2 2006
Modified from the driver which is originally written by Donald Becker.
@@ -17,9 +17,10 @@
SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
preliminary Rev. 1.0 Jan. 18, 1998
+ Rev 1.08.10 Apr. 2 2006 Daniele Venzano add vlan (jumbo packets) support
Rev 1.08.09 Sep. 19 2005 Daniele Venzano add Wake on LAN support
Rev 1.08.08 Jan. 22 2005 Daniele Venzano use netif_msg for debugging messages
- Rev 1.08.07 Nov. 2 2003 Daniele Venzano <webvenza@libero.it> add suspend/resume support
+ Rev 1.08.07 Nov. 2 2003 Daniele Venzano <venza@brownhat.org> add suspend/resume support
Rev 1.08.06 Sep. 24 2002 Mufasa Yang bug fix for Tx timeout & add SiS963 support
Rev 1.08.05 Jun. 6 2002 Mufasa Yang bug fix for read_eeprom & Tx descriptor over-boundary
Rev 1.08.04 Apr. 25 2002 Mufasa Yang <mufasa@sis.com.tw> added SiS962 support
@@ -77,7 +78,7 @@
#include "sis900.h"
#define SIS900_MODULE_NAME "sis900"
-#define SIS900_DRV_VERSION "v1.08.09 Sep. 19 2005"
+#define SIS900_DRV_VERSION "v1.08.10 Apr. 2 2006"
static char version[] __devinitdata =
KERN_INFO "sis900.c: " SIS900_DRV_VERSION "\n";
@@ -1401,6 +1402,11 @@
rx_flags |= RxATX;
}
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+ /* Can accept Jumbo packet */
+ rx_flags |= RxAJAB;
+#endif
+
outl (tx_flags, ioaddr + txcfg);
outl (rx_flags, ioaddr + rxcfg);
}
@@ -1713,18 +1719,26 @@
while (rx_status & OWN) {
unsigned int rx_size;
+ unsigned int data_size;
if (--rx_work_limit < 0)
break;
- rx_size = (rx_status & DSIZE) - CRC_SIZE;
+ data_size = rx_status & DSIZE;
+ rx_size = data_size - CRC_SIZE;
+
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+ /* ``TOOLONG'' flag means jumbo packet recived. */
+ if ((rx_status & TOOLONG) && data_size <= MAX_FRAME_SIZE)
+ rx_status &= (~ ((unsigned int)TOOLONG));
+#endif
if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {
/* corrupted packet received */
if (netif_msg_rx_err(sis_priv))
printk(KERN_DEBUG "%s: Corrupted packet "
- "received, buffer status = 0x%8.8x.\n",
- net_dev->name, rx_status);
+ "received, buffer status = 0x%8.8x/%d.\n",
+ net_dev->name, rx_status, data_size);
sis_priv->stats.rx_errors++;
if (rx_status & OVERRUN)
sis_priv->stats.rx_over_errors++;
diff --git a/drivers/net/sis900.h b/drivers/net/sis900.h
index 5032394..4834e3a 100644
--- a/drivers/net/sis900.h
+++ b/drivers/net/sis900.h
@@ -310,8 +310,14 @@
#define CRC_SIZE 4
#define MAC_HEADER_SIZE 14
-#define TX_BUF_SIZE 1536
-#define RX_BUF_SIZE 1536
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#define MAX_FRAME_SIZE (1518 + 4)
+#else
+#define MAX_FRAME_SIZE 1518
+#endif /* CONFIG_VLAN_802_1Q */
+
+#define TX_BUF_SIZE (MAX_FRAME_SIZE+18)
+#define RX_BUF_SIZE (MAX_FRAME_SIZE+18)
#define NUM_TX_DESC 16 /* Number of Tx descriptor registers. */
#define NUM_RX_DESC 16 /* Number of Rx descriptor registers. */
diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c
new file mode 100644
index 0000000..bdd8702
--- /dev/null
+++ b/drivers/net/smc911x.c
@@ -0,0 +1,2307 @@
+/*
+ * smc911x.c
+ * This is a driver for SMSC's LAN911{5,6,7,8} single-chip Ethernet devices.
+ *
+ * Copyright (C) 2005 Sensoria Corp
+ * Derived from the unified SMC91x driver by Nicolas Pitre
+ * and the smsc911x.c reference driver by SMSC
+ *
+ * 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.
+ *
+ * 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
+ *
+ * Arguments:
+ * watchdog = TX watchdog timeout
+ * tx_fifo_kb = Size of TX FIFO in KB
+ *
+ * History:
+ * 04/16/05 Dustin McIntire Initial version
+ */
+static const char version[] =
+ "smc911x.c: v1.0 04-16-2005 by Dustin McIntire <dustin@sensoria.com>\n";
+
+/* Debugging options */
+#define ENABLE_SMC_DEBUG_RX 0
+#define ENABLE_SMC_DEBUG_TX 0
+#define ENABLE_SMC_DEBUG_DMA 0
+#define ENABLE_SMC_DEBUG_PKTS 0
+#define ENABLE_SMC_DEBUG_MISC 0
+#define ENABLE_SMC_DEBUG_FUNC 0
+
+#define SMC_DEBUG_RX ((ENABLE_SMC_DEBUG_RX ? 1 : 0) << 0)
+#define SMC_DEBUG_TX ((ENABLE_SMC_DEBUG_TX ? 1 : 0) << 1)
+#define SMC_DEBUG_DMA ((ENABLE_SMC_DEBUG_DMA ? 1 : 0) << 2)
+#define SMC_DEBUG_PKTS ((ENABLE_SMC_DEBUG_PKTS ? 1 : 0) << 3)
+#define SMC_DEBUG_MISC ((ENABLE_SMC_DEBUG_MISC ? 1 : 0) << 4)
+#define SMC_DEBUG_FUNC ((ENABLE_SMC_DEBUG_FUNC ? 1 : 0) << 5)
+
+#ifndef SMC_DEBUG
+#define SMC_DEBUG ( SMC_DEBUG_RX | \
+ SMC_DEBUG_TX | \
+ SMC_DEBUG_DMA | \
+ SMC_DEBUG_PKTS | \
+ SMC_DEBUG_MISC | \
+ SMC_DEBUG_FUNC \
+ )
+#endif
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/crc32.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/workqueue.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "smc911x.h"
+
+/*
+ * Transmit timeout, default 5 seconds.
+ */
+static int watchdog = 5000;
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
+
+static int tx_fifo_kb=8;
+module_param(tx_fifo_kb, int, 0400);
+MODULE_PARM_DESC(tx_fifo_kb,"transmit FIFO size in KB (1<x<15)(default=8)");
+
+MODULE_LICENSE("GPL");
+
+/*
+ * The internal workings of the driver. If you are changing anything
+ * here with the SMC stuff, you should have the datasheet and know
+ * what you are doing.
+ */
+#define CARDNAME "smc911x"
+
+/*
+ * Use power-down feature of the chip
+ */
+#define POWER_DOWN 1
+
+
+/* store this information for the driver.. */
+struct smc911x_local {
+ /*
+ * If I have to wait until the DMA is finished and ready to reload a
+ * packet, I will store the skbuff here. Then, the DMA will send it
+ * out and free it.
+ */
+ struct sk_buff *pending_tx_skb;
+
+ /*
+ * these are things that the kernel wants me to keep, so users
+ * can find out semi-useless statistics of how well the card is
+ * performing
+ */
+ struct net_device_stats stats;
+
+ /* version/revision of the SMC911x chip */
+ u16 version;
+ u16 revision;
+
+ /* FIFO sizes */
+ int tx_fifo_kb;
+ int tx_fifo_size;
+ int rx_fifo_size;
+ int afc_cfg;
+
+ /* Contains the current active receive/phy mode */
+ int ctl_rfduplx;
+ int ctl_rspeed;
+
+ u32 msg_enable;
+ u32 phy_type;
+ struct mii_if_info mii;
+
+ /* work queue */
+ struct work_struct phy_configure;
+ int work_pending;
+
+ int tx_throttle;
+ spinlock_t lock;
+
+#ifdef SMC_USE_DMA
+ /* DMA needs the physical address of the chip */
+ u_long physaddr;
+ int rxdma;
+ int txdma;
+ int rxdma_active;
+ int txdma_active;
+ struct sk_buff *current_rx_skb;
+ struct sk_buff *current_tx_skb;
+ struct device *dev;
+#endif
+};
+
+#if SMC_DEBUG > 0
+#define DBG(n, args...) \
+ do { \
+ if (SMC_DEBUG & (n)) \
+ printk(args); \
+ } while (0)
+
+#define PRINTK(args...) printk(args)
+#else
+#define DBG(n, args...) do { } while (0)
+#define PRINTK(args...) printk(KERN_DEBUG args)
+#endif
+
+#if SMC_DEBUG_PKTS > 0
+static void PRINT_PKT(u_char *buf, int length)
+{
+ int i;
+ int remainder;
+ int lines;
+
+ lines = length / 16;
+ remainder = length % 16;
+
+ for (i = 0; i < lines ; i ++) {
+ int cur;
+ for (cur = 0; cur < 8; cur++) {
+ u_char a, b;
+ a = *buf++;
+ b = *buf++;
+ printk("%02x%02x ", a, b);
+ }
+ printk("\n");
+ }
+ for (i = 0; i < remainder/2 ; i++) {
+ u_char a, b;
+ a = *buf++;
+ b = *buf++;
+ printk("%02x%02x ", a, b);
+ }
+ printk("\n");
+}
+#else
+#define PRINT_PKT(x...) do { } while (0)
+#endif
+
+
+/* this enables an interrupt in the interrupt mask register */
+#define SMC_ENABLE_INT(x) do { \
+ unsigned int __mask; \
+ unsigned long __flags; \
+ spin_lock_irqsave(&lp->lock, __flags); \
+ __mask = SMC_GET_INT_EN(); \
+ __mask |= (x); \
+ SMC_SET_INT_EN(__mask); \
+ spin_unlock_irqrestore(&lp->lock, __flags); \
+} while (0)
+
+/* this disables an interrupt from the interrupt mask register */
+#define SMC_DISABLE_INT(x) do { \
+ unsigned int __mask; \
+ unsigned long __flags; \
+ spin_lock_irqsave(&lp->lock, __flags); \
+ __mask = SMC_GET_INT_EN(); \
+ __mask &= ~(x); \
+ SMC_SET_INT_EN(__mask); \
+ spin_unlock_irqrestore(&lp->lock, __flags); \
+} while (0)
+
+/*
+ * this does a soft reset on the device
+ */
+static void smc911x_reset(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int reg, timeout=0, resets=1;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ /* Take out of PM setting first */
+ if ((SMC_GET_PMT_CTRL() & PMT_CTRL_READY_) == 0) {
+ /* Write to the bytetest will take out of powerdown */
+ SMC_SET_BYTE_TEST(0);
+ timeout=10;
+ do {
+ udelay(10);
+ reg = SMC_GET_PMT_CTRL() & PMT_CTRL_READY_;
+ } while ( timeout-- && !reg);
+ if (timeout == 0) {
+ PRINTK("%s: smc911x_reset timeout waiting for PM restore\n", dev->name);
+ return;
+ }
+ }
+
+ /* Disable all interrupts */
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_SET_INT_EN(0);
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ while (resets--) {
+ SMC_SET_HW_CFG(HW_CFG_SRST_);
+ timeout=10;
+ do {
+ udelay(10);
+ reg = SMC_GET_HW_CFG();
+ /* If chip indicates reset timeout then try again */
+ if (reg & HW_CFG_SRST_TO_) {
+ PRINTK("%s: chip reset timeout, retrying...\n", dev->name);
+ resets++;
+ break;
+ }
+ } while ( timeout-- && (reg & HW_CFG_SRST_));
+ }
+ if (timeout == 0) {
+ PRINTK("%s: smc911x_reset timeout waiting for reset\n", dev->name);
+ return;
+ }
+
+ /* make sure EEPROM has finished loading before setting GPIO_CFG */
+ timeout=1000;
+ while ( timeout-- && (SMC_GET_E2P_CMD() & E2P_CMD_EPC_BUSY_)) {
+ udelay(10);
+ }
+ if (timeout == 0){
+ PRINTK("%s: smc911x_reset timeout waiting for EEPROM busy\n", dev->name);
+ return;
+ }
+
+ /* Initialize interrupts */
+ SMC_SET_INT_EN(0);
+ SMC_ACK_INT(-1);
+
+ /* Reset the FIFO level and flow control settings */
+ SMC_SET_HW_CFG((lp->tx_fifo_kb & 0xF) << 16);
+//TODO: Figure out what appropriate pause time is
+ SMC_SET_FLOW(FLOW_FCPT_ | FLOW_FCEN_);
+ SMC_SET_AFC_CFG(lp->afc_cfg);
+
+
+ /* Set to LED outputs */
+ SMC_SET_GPIO_CFG(0x70070000);
+
+ /*
+ * Deassert IRQ for 1*10us for edge type interrupts
+ * and drive IRQ pin push-pull
+ */
+ SMC_SET_IRQ_CFG( (1 << 24) | INT_CFG_IRQ_EN_ | INT_CFG_IRQ_TYPE_ );
+
+ /* clear anything saved */
+ if (lp->pending_tx_skb != NULL) {
+ dev_kfree_skb (lp->pending_tx_skb);
+ lp->pending_tx_skb = NULL;
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+ }
+}
+
+/*
+ * Enable Interrupts, Receive, and Transmit
+ */
+static void smc911x_enable(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned mask, cfg, cr;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ SMC_SET_MAC_ADDR(dev->dev_addr);
+
+ /* Enable TX */
+ cfg = SMC_GET_HW_CFG();
+ cfg &= HW_CFG_TX_FIF_SZ_ | 0xFFF;
+ cfg |= HW_CFG_SF_;
+ SMC_SET_HW_CFG(cfg);
+ SMC_SET_FIFO_TDA(0xFF);
+ /* Update TX stats on every 64 packets received or every 1 sec */
+ SMC_SET_FIFO_TSL(64);
+ SMC_SET_GPT_CFG(GPT_CFG_TIMER_EN_ | 10000);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_GET_MAC_CR(cr);
+ cr |= MAC_CR_TXEN_ | MAC_CR_HBDIS_;
+ SMC_SET_MAC_CR(cr);
+ SMC_SET_TX_CFG(TX_CFG_TX_ON_);
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ /* Add 2 byte padding to start of packets */
+ SMC_SET_RX_CFG((2<<8) & RX_CFG_RXDOFF_);
+
+ /* Turn on receiver and enable RX */
+ if (cr & MAC_CR_RXEN_)
+ DBG(SMC_DEBUG_RX, "%s: Receiver already enabled\n", dev->name);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_SET_MAC_CR( cr | MAC_CR_RXEN_ );
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ /* Interrupt on every received packet */
+ SMC_SET_FIFO_RSA(0x01);
+ SMC_SET_FIFO_RSL(0x00);
+
+ /* now, enable interrupts */
+ mask = INT_EN_TDFA_EN_ | INT_EN_TSFL_EN_ | INT_EN_RSFL_EN_ |
+ INT_EN_GPT_INT_EN_ | INT_EN_RXDFH_INT_EN_ | INT_EN_RXE_EN_ |
+ INT_EN_PHY_INT_EN_;
+ if (IS_REV_A(lp->revision))
+ mask|=INT_EN_RDFL_EN_;
+ else {
+ mask|=INT_EN_RDFO_EN_;
+ }
+ SMC_ENABLE_INT(mask);
+}
+
+/*
+ * this puts the device in an inactive state
+ */
+static void smc911x_shutdown(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned cr;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", CARDNAME, __FUNCTION__);
+
+ /* Disable IRQ's */
+ SMC_SET_INT_EN(0);
+
+ /* Turn of Rx and TX */
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_GET_MAC_CR(cr);
+ cr &= ~(MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_);
+ SMC_SET_MAC_CR(cr);
+ SMC_SET_TX_CFG(TX_CFG_STOP_TX_);
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+static inline void smc911x_drop_pkt(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int fifo_count, timeout, reg;
+
+ DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n", CARDNAME, __FUNCTION__);
+ fifo_count = SMC_GET_RX_FIFO_INF() & 0xFFFF;
+ if (fifo_count <= 4) {
+ /* Manually dump the packet data */
+ while (fifo_count--)
+ SMC_GET_RX_FIFO();
+ } else {
+ /* Fast forward through the bad packet */
+ SMC_SET_RX_DP_CTRL(RX_DP_CTRL_FFWD_BUSY_);
+ timeout=50;
+ do {
+ udelay(10);
+ reg = SMC_GET_RX_DP_CTRL() & RX_DP_CTRL_FFWD_BUSY_;
+ } while ( timeout-- && reg);
+ if (timeout == 0) {
+ PRINTK("%s: timeout waiting for RX fast forward\n", dev->name);
+ }
+ }
+}
+
+/*
+ * This is the procedure to handle the receipt of a packet.
+ * It should be called after checking for packet presence in
+ * the RX status FIFO. It must be called with the spin lock
+ * already held.
+ */
+static inline void smc911x_rcv(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int pkt_len, status;
+ struct sk_buff *skb;
+ unsigned char *data;
+
+ DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n",
+ dev->name, __FUNCTION__);
+ status = SMC_GET_RX_STS_FIFO();
+ DBG(SMC_DEBUG_RX, "%s: Rx pkt len %d status 0x%08x \n",
+ dev->name, (status & 0x3fff0000) >> 16, status & 0xc000ffff);
+ pkt_len = (status & RX_STS_PKT_LEN_) >> 16;
+ if (status & RX_STS_ES_) {
+ /* Deal with a bad packet */
+ lp->stats.rx_errors++;
+ if (status & RX_STS_CRC_ERR_)
+ lp->stats.rx_crc_errors++;
+ else {
+ if (status & RX_STS_LEN_ERR_)
+ lp->stats.rx_length_errors++;
+ if (status & RX_STS_MCAST_)
+ lp->stats.multicast++;
+ }
+ /* Remove the bad packet data from the RX FIFO */
+ smc911x_drop_pkt(dev);
+ } else {
+ /* Receive a valid packet */
+ /* Alloc a buffer with extra room for DMA alignment */
+ skb=dev_alloc_skb(pkt_len+32);
+ if (unlikely(skb == NULL)) {
+ PRINTK( "%s: Low memory, rcvd packet dropped.\n",
+ dev->name);
+ lp->stats.rx_dropped++;
+ smc911x_drop_pkt(dev);
+ return;
+ }
+ /* Align IP header to 32 bits
+ * Note that the device is configured to add a 2
+ * byte padding to the packet start, so we really
+ * want to write to the orignal data pointer */
+ data = skb->data;
+ skb_reserve(skb, 2);
+ skb_put(skb,pkt_len-4);
+#ifdef SMC_USE_DMA
+ {
+ unsigned int fifo;
+ /* Lower the FIFO threshold if possible */
+ fifo = SMC_GET_FIFO_INT();
+ if (fifo & 0xFF) fifo--;
+ DBG(SMC_DEBUG_RX, "%s: Setting RX stat FIFO threshold to %d\n",
+ dev->name, fifo & 0xff);
+ SMC_SET_FIFO_INT(fifo);
+ /* Setup RX DMA */
+ SMC_SET_RX_CFG(RX_CFG_RX_END_ALGN16_ | ((2<<8) & RX_CFG_RXDOFF_));
+ lp->rxdma_active = 1;
+ lp->current_rx_skb = skb;
+ SMC_PULL_DATA(data, (pkt_len+2+15) & ~15);
+ /* Packet processing deferred to DMA RX interrupt */
+ }
+#else
+ SMC_SET_RX_CFG(RX_CFG_RX_END_ALGN4_ | ((2<<8) & RX_CFG_RXDOFF_));
+ SMC_PULL_DATA(data, pkt_len+2+3);
+
+ DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name,);
+ PRINT_PKT(data, ((pkt_len - 4) <= 64) ? pkt_len - 4 : 64);
+ dev->last_rx = jiffies;
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += pkt_len-4;
+#endif
+ }
+}
+
+/*
+ * This is called to actually send a packet to the chip.
+ */
+static void smc911x_hardware_send_pkt(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ struct sk_buff *skb;
+ unsigned int cmdA, cmdB, len;
+ unsigned char *buf;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n", dev->name, __FUNCTION__);
+ BUG_ON(lp->pending_tx_skb == NULL);
+
+ skb = lp->pending_tx_skb;
+ lp->pending_tx_skb = NULL;
+
+ /* cmdA {25:24] data alignment [20:16] start offset [10:0] buffer length */
+ /* cmdB {31:16] pkt tag [10:0] length */
+#ifdef SMC_USE_DMA
+ /* 16 byte buffer alignment mode */
+ buf = (char*)((u32)(skb->data) & ~0xF);
+ len = (skb->len + 0xF + ((u32)skb->data & 0xF)) & ~0xF;
+ cmdA = (1<<24) | (((u32)skb->data & 0xF)<<16) |
+ TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ |
+ skb->len;
+#else
+ buf = (char*)((u32)skb->data & ~0x3);
+ len = (skb->len + 3 + ((u32)skb->data & 3)) & ~0x3;
+ cmdA = (((u32)skb->data & 0x3) << 16) |
+ TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ |
+ skb->len;
+#endif
+ /* tag is packet length so we can use this in stats update later */
+ cmdB = (skb->len << 16) | (skb->len & 0x7FF);
+
+ DBG(SMC_DEBUG_TX, "%s: TX PKT LENGTH 0x%04x (%d) BUF 0x%p CMDA 0x%08x CMDB 0x%08x\n",
+ dev->name, len, len, buf, cmdA, cmdB);
+ SMC_SET_TX_FIFO(cmdA);
+ SMC_SET_TX_FIFO(cmdB);
+
+ DBG(SMC_DEBUG_PKTS, "%s: Transmitted packet\n", dev->name);
+ PRINT_PKT(buf, len <= 64 ? len : 64);
+
+ /* Send pkt via PIO or DMA */
+#ifdef SMC_USE_DMA
+ lp->current_tx_skb = skb;
+ SMC_PUSH_DATA(buf, len);
+ /* DMA complete IRQ will free buffer and set jiffies */
+#else
+ SMC_PUSH_DATA(buf, len);
+ dev->trans_start = jiffies;
+ dev_kfree_skb(skb);
+#endif
+ spin_lock_irqsave(&lp->lock, flags);
+ if (!lp->tx_throttle) {
+ netif_wake_queue(dev);
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+ SMC_ENABLE_INT(INT_EN_TDFA_EN_ | INT_EN_TSFL_EN_);
+}
+
+/*
+ * Since I am not sure if I will have enough room in the chip's ram
+ * to store the packet, I call this routine which either sends it
+ * now, or set the card to generates an interrupt when ready
+ * for the packet.
+ */
+static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int free;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n",
+ dev->name, __FUNCTION__);
+
+ BUG_ON(lp->pending_tx_skb != NULL);
+
+ free = SMC_GET_TX_FIFO_INF() & TX_FIFO_INF_TDFREE_;
+ DBG(SMC_DEBUG_TX, "%s: TX free space %d\n", dev->name, free);
+
+ /* Turn off the flow when running out of space in FIFO */
+ if (free <= SMC911X_TX_FIFO_LOW_THRESHOLD) {
+ DBG(SMC_DEBUG_TX, "%s: Disabling data flow due to low FIFO space (%d)\n",
+ dev->name, free);
+ spin_lock_irqsave(&lp->lock, flags);
+ /* Reenable when at least 1 packet of size MTU present */
+ SMC_SET_FIFO_TDA((SMC911X_TX_FIFO_LOW_THRESHOLD)/64);
+ lp->tx_throttle = 1;
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+
+ /* Drop packets when we run out of space in TX FIFO
+ * Account for overhead required for:
+ *
+ * Tx command words 8 bytes
+ * Start offset 15 bytes
+ * End padding 15 bytes
+ */
+ if (unlikely(free < (skb->len + 8 + 15 + 15))) {
+ printk("%s: No Tx free space %d < %d\n",
+ dev->name, free, skb->len);
+ lp->pending_tx_skb = NULL;
+ lp->stats.tx_errors++;
+ lp->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+#ifdef SMC_USE_DMA
+ {
+ /* If the DMA is already running then defer this packet Tx until
+ * the DMA IRQ starts it
+ */
+ spin_lock_irqsave(&lp->lock, flags);
+ if (lp->txdma_active) {
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Tx DMA running, deferring packet\n", dev->name);
+ lp->pending_tx_skb = skb;
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return 0;
+ } else {
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Activating Tx DMA\n", dev->name);
+ lp->txdma_active = 1;
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+#endif
+ lp->pending_tx_skb = skb;
+ smc911x_hardware_send_pkt(dev);
+
+ return 0;
+}
+
+/*
+ * This handles a TX status interrupt, which is only called when:
+ * - a TX error occurred, or
+ * - TX of a packet completed.
+ */
+static void smc911x_tx(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int tx_status;
+
+ DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n",
+ dev->name, __FUNCTION__);
+
+ /* Collect the TX status */
+ while (((SMC_GET_TX_FIFO_INF() & TX_FIFO_INF_TSUSED_) >> 16) != 0) {
+ DBG(SMC_DEBUG_TX, "%s: Tx stat FIFO used 0x%04x\n",
+ dev->name,
+ (SMC_GET_TX_FIFO_INF() & TX_FIFO_INF_TSUSED_) >> 16);
+ tx_status = SMC_GET_TX_STS_FIFO();
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes+=tx_status>>16;
+ DBG(SMC_DEBUG_TX, "%s: Tx FIFO tag 0x%04x status 0x%04x\n",
+ dev->name, (tx_status & 0xffff0000) >> 16,
+ tx_status & 0x0000ffff);
+ /* count Tx errors, but ignore lost carrier errors when in
+ * full-duplex mode */
+ if ((tx_status & TX_STS_ES_) && !(lp->ctl_rfduplx &&
+ !(tx_status & 0x00000306))) {
+ lp->stats.tx_errors++;
+ }
+ if (tx_status & TX_STS_MANY_COLL_) {
+ lp->stats.collisions+=16;
+ lp->stats.tx_aborted_errors++;
+ } else {
+ lp->stats.collisions+=(tx_status & TX_STS_COLL_CNT_) >> 3;
+ }
+ /* carrier error only has meaning for half-duplex communication */
+ if ((tx_status & (TX_STS_LOC_ | TX_STS_NO_CARR_)) &&
+ !lp->ctl_rfduplx) {
+ lp->stats.tx_carrier_errors++;
+ }
+ if (tx_status & TX_STS_LATE_COLL_) {
+ lp->stats.collisions++;
+ lp->stats.tx_aborted_errors++;
+ }
+ }
+}
+
+
+/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
+/*
+ * Reads a register from the MII Management serial interface
+ */
+
+static int smc911x_phy_read(struct net_device *dev, int phyaddr, int phyreg)
+{
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int phydata;
+
+ SMC_GET_MII(phyreg, phyaddr, phydata);
+
+ DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%02x, phydata=0x%04x\n",
+ __FUNCTION__, phyaddr, phyreg, phydata);
+ return phydata;
+}
+
+
+/*
+ * Writes a register to the MII Management serial interface
+ */
+static void smc911x_phy_write(struct net_device *dev, int phyaddr, int phyreg,
+ int phydata)
+{
+ unsigned long ioaddr = dev->base_addr;
+
+ DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+ __FUNCTION__, phyaddr, phyreg, phydata);
+
+ SMC_SET_MII(phyreg, phyaddr, phydata);
+}
+
+/*
+ * Finds and reports the PHY address (115 and 117 have external
+ * PHY interface 118 has internal only
+ */
+static void smc911x_phy_detect(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct smc911x_local *lp = netdev_priv(dev);
+ int phyaddr;
+ unsigned int cfg, id1, id2;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ lp->phy_type = 0;
+
+ /*
+ * Scan all 32 PHY addresses if necessary, starting at
+ * PHY#1 to PHY#31, and then PHY#0 last.
+ */
+ switch(lp->version) {
+ case 0x115:
+ case 0x117:
+ cfg = SMC_GET_HW_CFG();
+ if (cfg & HW_CFG_EXT_PHY_DET_) {
+ cfg &= ~HW_CFG_PHY_CLK_SEL_;
+ cfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ SMC_SET_HW_CFG(cfg);
+ udelay(10); /* Wait for clocks to stop */
+
+ cfg |= HW_CFG_EXT_PHY_EN_;
+ SMC_SET_HW_CFG(cfg);
+ udelay(10); /* Wait for clocks to stop */
+
+ cfg &= ~HW_CFG_PHY_CLK_SEL_;
+ cfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+ SMC_SET_HW_CFG(cfg);
+ udelay(10); /* Wait for clocks to stop */
+
+ cfg |= HW_CFG_SMI_SEL_;
+ SMC_SET_HW_CFG(cfg);
+
+ for (phyaddr = 1; phyaddr < 32; ++phyaddr) {
+
+ /* Read the PHY identifiers */
+ SMC_GET_PHY_ID1(phyaddr & 31, id1);
+ SMC_GET_PHY_ID2(phyaddr & 31, id2);
+
+ /* Make sure it is a valid identifier */
+ if (id1 != 0x0000 && id1 != 0xffff &&
+ id1 != 0x8000 && id2 != 0x0000 &&
+ id2 != 0xffff && id2 != 0x8000) {
+ /* Save the PHY's address */
+ lp->mii.phy_id = phyaddr & 31;
+ lp->phy_type = id1 << 16 | id2;
+ break;
+ }
+ }
+ }
+ default:
+ /* Internal media only */
+ SMC_GET_PHY_ID1(1, id1);
+ SMC_GET_PHY_ID2(1, id2);
+ /* Save the PHY's address */
+ lp->mii.phy_id = 1;
+ lp->phy_type = id1 << 16 | id2;
+ }
+
+ DBG(SMC_DEBUG_MISC, "%s: phy_id1=0x%x, phy_id2=0x%x phyaddr=0x%d\n",
+ dev->name, id1, id2, lp->mii.phy_id);
+}
+
+/*
+ * Sets the PHY to a configuration as determined by the user.
+ * Called with spin_lock held.
+ */
+static int smc911x_phy_fixed(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ int phyaddr = lp->mii.phy_id;
+ int bmcr;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ /* Enter Link Disable state */
+ SMC_GET_PHY_BMCR(phyaddr, bmcr);
+ bmcr |= BMCR_PDOWN;
+ SMC_SET_PHY_BMCR(phyaddr, bmcr);
+
+ /*
+ * Set our fixed capabilities
+ * Disable auto-negotiation
+ */
+ bmcr &= ~BMCR_ANENABLE;
+ if (lp->ctl_rfduplx)
+ bmcr |= BMCR_FULLDPLX;
+
+ if (lp->ctl_rspeed == 100)
+ bmcr |= BMCR_SPEED100;
+
+ /* Write our capabilities to the phy control register */
+ SMC_SET_PHY_BMCR(phyaddr, bmcr);
+
+ /* Re-Configure the Receive/Phy Control register */
+ bmcr &= ~BMCR_PDOWN;
+ SMC_SET_PHY_BMCR(phyaddr, bmcr);
+
+ return 1;
+}
+
+/*
+ * smc911x_phy_reset - reset the phy
+ * @dev: net device
+ * @phy: phy address
+ *
+ * Issue a software reset for the specified PHY and
+ * wait up to 100ms for the reset to complete. We should
+ * not access the PHY for 50ms after issuing the reset.
+ *
+ * The time to wait appears to be dependent on the PHY.
+ *
+ */
+static int smc911x_phy_reset(struct net_device *dev, int phy)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ int timeout;
+ unsigned long flags;
+ unsigned int reg;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __FUNCTION__);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ reg = SMC_GET_PMT_CTRL();
+ reg &= ~0xfffff030;
+ reg |= PMT_CTRL_PHY_RST_;
+ SMC_SET_PMT_CTRL(reg);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ for (timeout = 2; timeout; timeout--) {
+ msleep(50);
+ spin_lock_irqsave(&lp->lock, flags);
+ reg = SMC_GET_PMT_CTRL();
+ spin_unlock_irqrestore(&lp->lock, flags);
+ if (!(reg & PMT_CTRL_PHY_RST_)) {
+ /* extra delay required because the phy may
+ * not be completed with its reset
+ * when PHY_BCR_RESET_ is cleared. 256us
+ * should suffice, but use 500us to be safe
+ */
+ udelay(500);
+ break;
+ }
+ }
+
+ return reg & PMT_CTRL_PHY_RST_;
+}
+
+/*
+ * smc911x_phy_powerdown - powerdown phy
+ * @dev: net device
+ * @phy: phy address
+ *
+ * Power down the specified PHY
+ */
+static void smc911x_phy_powerdown(struct net_device *dev, int phy)
+{
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int bmcr;
+
+ /* Enter Link Disable state */
+ SMC_GET_PHY_BMCR(phy, bmcr);
+ bmcr |= BMCR_PDOWN;
+ SMC_SET_PHY_BMCR(phy, bmcr);
+}
+
+/*
+ * smc911x_phy_check_media - check the media status and adjust BMCR
+ * @dev: net device
+ * @init: set true for initialisation
+ *
+ * Select duplex mode depending on negotiation state. This
+ * also updates our carrier state.
+ */
+static void smc911x_phy_check_media(struct net_device *dev, int init)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ int phyaddr = lp->mii.phy_id;
+ unsigned int bmcr, cr;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) {
+ /* duplex state has changed */
+ SMC_GET_PHY_BMCR(phyaddr, bmcr);
+ SMC_GET_MAC_CR(cr);
+ if (lp->mii.full_duplex) {
+ DBG(SMC_DEBUG_MISC, "%s: Configuring for full-duplex mode\n", dev->name);
+ bmcr |= BMCR_FULLDPLX;
+ cr |= MAC_CR_RCVOWN_;
+ } else {
+ DBG(SMC_DEBUG_MISC, "%s: Configuring for half-duplex mode\n", dev->name);
+ bmcr &= ~BMCR_FULLDPLX;
+ cr &= ~MAC_CR_RCVOWN_;
+ }
+ SMC_SET_PHY_BMCR(phyaddr, bmcr);
+ SMC_SET_MAC_CR(cr);
+ }
+}
+
+/*
+ * Configures the specified PHY through the MII management interface
+ * using Autonegotiation.
+ * Calls smc911x_phy_fixed() if the user has requested a certain config.
+ * If RPC ANEG bit is set, the media selection is dependent purely on
+ * the selection by the MII (either in the MII BMCR reg or the result
+ * of autonegotiation.) If the RPC ANEG bit is cleared, the selection
+ * is controlled by the RPC SPEED and RPC DPLX bits.
+ */
+static void smc911x_phy_configure(void *data)
+{
+ struct net_device *dev = data;
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ int phyaddr = lp->mii.phy_id;
+ int my_phy_caps; /* My PHY capabilities */
+ int my_ad_caps; /* My Advertised capabilities */
+ int status;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __FUNCTION__);
+
+ /*
+ * We should not be called if phy_type is zero.
+ */
+ if (lp->phy_type == 0)
+ goto smc911x_phy_configure_exit;
+
+ if (smc911x_phy_reset(dev, phyaddr)) {
+ printk("%s: PHY reset timed out\n", dev->name);
+ goto smc911x_phy_configure_exit;
+ }
+ spin_lock_irqsave(&lp->lock, flags);
+
+ /*
+ * Enable PHY Interrupts (for register 18)
+ * Interrupts listed here are enabled
+ */
+ SMC_SET_PHY_INT_MASK(phyaddr, PHY_INT_MASK_ENERGY_ON_ |
+ PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_REMOTE_FAULT_ |
+ PHY_INT_MASK_LINK_DOWN_);
+
+ /* If the user requested no auto neg, then go set his request */
+ if (lp->mii.force_media) {
+ smc911x_phy_fixed(dev);
+ goto smc911x_phy_configure_exit;
+ }
+
+ /* Copy our capabilities from MII_BMSR to MII_ADVERTISE */
+ SMC_GET_PHY_BMSR(phyaddr, my_phy_caps);
+ if (!(my_phy_caps & BMSR_ANEGCAPABLE)) {
+ printk(KERN_INFO "Auto negotiation NOT supported\n");
+ smc911x_phy_fixed(dev);
+ goto smc911x_phy_configure_exit;
+ }
+
+ /* CSMA capable w/ both pauses */
+ my_ad_caps = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+
+ if (my_phy_caps & BMSR_100BASE4)
+ my_ad_caps |= ADVERTISE_100BASE4;
+ if (my_phy_caps & BMSR_100FULL)
+ my_ad_caps |= ADVERTISE_100FULL;
+ if (my_phy_caps & BMSR_100HALF)
+ my_ad_caps |= ADVERTISE_100HALF;
+ if (my_phy_caps & BMSR_10FULL)
+ my_ad_caps |= ADVERTISE_10FULL;
+ if (my_phy_caps & BMSR_10HALF)
+ my_ad_caps |= ADVERTISE_10HALF;
+
+ /* Disable capabilities not selected by our user */
+ if (lp->ctl_rspeed != 100)
+ my_ad_caps &= ~(ADVERTISE_100BASE4|ADVERTISE_100FULL|ADVERTISE_100HALF);
+
+ if (!lp->ctl_rfduplx)
+ my_ad_caps &= ~(ADVERTISE_100FULL|ADVERTISE_10FULL);
+
+ /* Update our Auto-Neg Advertisement Register */
+ SMC_SET_PHY_MII_ADV(phyaddr, my_ad_caps);
+ lp->mii.advertising = my_ad_caps;
+
+ /*
+ * Read the register back. Without this, it appears that when
+ * auto-negotiation is restarted, sometimes it isn't ready and
+ * the link does not come up.
+ */
+ udelay(10);
+ SMC_GET_PHY_MII_ADV(phyaddr, status);
+
+ DBG(SMC_DEBUG_MISC, "%s: phy caps=0x%04x\n", dev->name, my_phy_caps);
+ DBG(SMC_DEBUG_MISC, "%s: phy advertised caps=0x%04x\n", dev->name, my_ad_caps);
+
+ /* Restart auto-negotiation process in order to advertise my caps */
+ SMC_SET_PHY_BMCR(phyaddr, BMCR_ANENABLE | BMCR_ANRESTART);
+
+ smc911x_phy_check_media(dev, 1);
+
+smc911x_phy_configure_exit:
+ spin_unlock_irqrestore(&lp->lock, flags);
+ lp->work_pending = 0;
+}
+
+/*
+ * smc911x_phy_interrupt
+ *
+ * Purpose: Handle interrupts relating to PHY register 18. This is
+ * called from the "hard" interrupt handler under our private spinlock.
+ */
+static void smc911x_phy_interrupt(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ int phyaddr = lp->mii.phy_id;
+ int status;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ if (lp->phy_type == 0)
+ return;
+
+ smc911x_phy_check_media(dev, 0);
+ /* read to clear status bits */
+ SMC_GET_PHY_INT_SRC(phyaddr,status);
+ DBG(SMC_DEBUG_MISC, "%s: PHY interrupt status 0x%04x\n",
+ dev->name, status & 0xffff);
+ DBG(SMC_DEBUG_MISC, "%s: AFC_CFG 0x%08x\n",
+ dev->name, SMC_GET_AFC_CFG());
+}
+
+/*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*/
+
+/*
+ * This is the main routine of the driver, to handle the device when
+ * it needs some attention.
+ */
+static irqreturn_t smc911x_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ unsigned long ioaddr = dev->base_addr;
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int status, mask, timeout;
+ unsigned int rx_overrun=0, cr, pkts;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ /* Spurious interrupt check */
+ if ((SMC_GET_IRQ_CFG() & (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) !=
+ (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) {
+ return IRQ_NONE;
+ }
+
+ mask = SMC_GET_INT_EN();
+ SMC_SET_INT_EN(0);
+
+ /* set a timeout value, so I don't stay here forever */
+ timeout = 8;
+
+
+ do {
+ status = SMC_GET_INT();
+
+ DBG(SMC_DEBUG_MISC, "%s: INT 0x%08x MASK 0x%08x OUTSIDE MASK 0x%08x\n",
+ dev->name, status, mask, status & ~mask);
+
+ status &= mask;
+ if (!status)
+ break;
+
+ /* Handle SW interrupt condition */
+ if (status & INT_STS_SW_INT_) {
+ SMC_ACK_INT(INT_STS_SW_INT_);
+ mask &= ~INT_EN_SW_INT_EN_;
+ }
+ /* Handle various error conditions */
+ if (status & INT_STS_RXE_) {
+ SMC_ACK_INT(INT_STS_RXE_);
+ lp->stats.rx_errors++;
+ }
+ if (status & INT_STS_RXDFH_INT_) {
+ SMC_ACK_INT(INT_STS_RXDFH_INT_);
+ lp->stats.rx_dropped+=SMC_GET_RX_DROP();
+ }
+ /* Undocumented interrupt-what is the right thing to do here? */
+ if (status & INT_STS_RXDF_INT_) {
+ SMC_ACK_INT(INT_STS_RXDF_INT_);
+ }
+
+ /* Rx Data FIFO exceeds set level */
+ if (status & INT_STS_RDFL_) {
+ if (IS_REV_A(lp->revision)) {
+ rx_overrun=1;
+ SMC_GET_MAC_CR(cr);
+ cr &= ~MAC_CR_RXEN_;
+ SMC_SET_MAC_CR(cr);
+ DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name);
+ lp->stats.rx_errors++;
+ lp->stats.rx_fifo_errors++;
+ }
+ SMC_ACK_INT(INT_STS_RDFL_);
+ }
+ if (status & INT_STS_RDFO_) {
+ if (!IS_REV_A(lp->revision)) {
+ SMC_GET_MAC_CR(cr);
+ cr &= ~MAC_CR_RXEN_;
+ SMC_SET_MAC_CR(cr);
+ rx_overrun=1;
+ DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name);
+ lp->stats.rx_errors++;
+ lp->stats.rx_fifo_errors++;
+ }
+ SMC_ACK_INT(INT_STS_RDFO_);
+ }
+ /* Handle receive condition */
+ if ((status & INT_STS_RSFL_) || rx_overrun) {
+ unsigned int fifo;
+ DBG(SMC_DEBUG_RX, "%s: RX irq\n", dev->name);
+ fifo = SMC_GET_RX_FIFO_INF();
+ pkts = (fifo & RX_FIFO_INF_RXSUSED_) >> 16;
+ DBG(SMC_DEBUG_RX, "%s: Rx FIFO pkts %d, bytes %d\n",
+ dev->name, pkts, fifo & 0xFFFF );
+ if (pkts != 0) {
+#ifdef SMC_USE_DMA
+ unsigned int fifo;
+ if (lp->rxdma_active){
+ DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA,
+ "%s: RX DMA active\n", dev->name);
+ /* The DMA is already running so up the IRQ threshold */
+ fifo = SMC_GET_FIFO_INT() & ~0xFF;
+ fifo |= pkts & 0xFF;
+ DBG(SMC_DEBUG_RX,
+ "%s: Setting RX stat FIFO threshold to %d\n",
+ dev->name, fifo & 0xff);
+ SMC_SET_FIFO_INT(fifo);
+ } else
+#endif
+ smc911x_rcv(dev);
+ }
+ SMC_ACK_INT(INT_STS_RSFL_);
+ }
+ /* Handle transmit FIFO available */
+ if (status & INT_STS_TDFA_) {
+ DBG(SMC_DEBUG_TX, "%s: TX data FIFO space available irq\n", dev->name);
+ SMC_SET_FIFO_TDA(0xFF);
+ lp->tx_throttle = 0;
+#ifdef SMC_USE_DMA
+ if (!lp->txdma_active)
+#endif
+ netif_wake_queue(dev);
+ SMC_ACK_INT(INT_STS_TDFA_);
+ }
+ /* Handle transmit done condition */
+#if 1
+ if (status & (INT_STS_TSFL_ | INT_STS_GPT_INT_)) {
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_MISC,
+ "%s: Tx stat FIFO limit (%d) /GPT irq\n",
+ dev->name, (SMC_GET_FIFO_INT() & 0x00ff0000) >> 16);
+ smc911x_tx(dev);
+ SMC_SET_GPT_CFG(GPT_CFG_TIMER_EN_ | 10000);
+ SMC_ACK_INT(INT_STS_TSFL_);
+ SMC_ACK_INT(INT_STS_TSFL_ | INT_STS_GPT_INT_);
+ }
+#else
+ if (status & INT_STS_TSFL_) {
+ DBG(SMC_DEBUG_TX, "%s: TX status FIFO limit (%d) irq \n", dev->name, );
+ smc911x_tx(dev);
+ SMC_ACK_INT(INT_STS_TSFL_);
+ }
+
+ if (status & INT_STS_GPT_INT_) {
+ DBG(SMC_DEBUG_RX, "%s: IRQ_CFG 0x%08x FIFO_INT 0x%08x RX_CFG 0x%08x\n",
+ dev->name,
+ SMC_GET_IRQ_CFG(),
+ SMC_GET_FIFO_INT(),
+ SMC_GET_RX_CFG());
+ DBG(SMC_DEBUG_RX, "%s: Rx Stat FIFO Used 0x%02x "
+ "Data FIFO Used 0x%04x Stat FIFO 0x%08x\n",
+ dev->name,
+ (SMC_GET_RX_FIFO_INF() & 0x00ff0000) >> 16,
+ SMC_GET_RX_FIFO_INF() & 0xffff,
+ SMC_GET_RX_STS_FIFO_PEEK());
+ SMC_SET_GPT_CFG(GPT_CFG_TIMER_EN_ | 10000);
+ SMC_ACK_INT(INT_STS_GPT_INT_);
+ }
+#endif
+
+ /* Handle PHY interupt condition */
+ if (status & INT_STS_PHY_INT_) {
+ DBG(SMC_DEBUG_MISC, "%s: PHY irq\n", dev->name);
+ smc911x_phy_interrupt(dev);
+ SMC_ACK_INT(INT_STS_PHY_INT_);
+ }
+ } while (--timeout);
+
+ /* restore mask state */
+ SMC_SET_INT_EN(mask);
+
+ DBG(SMC_DEBUG_MISC, "%s: Interrupt done (%d loops)\n",
+ dev->name, 8-timeout);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ DBG(3, "%s: Interrupt done (%d loops)\n", dev->name, 8-timeout);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef SMC_USE_DMA
+static void
+smc911x_tx_dma_irq(int dma, void *data, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct smc911x_local *lp = netdev_priv(dev);
+ struct sk_buff *skb = lp->current_tx_skb;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: TX DMA irq handler\n", dev->name);
+ /* Clear the DMA interrupt sources */
+ SMC_DMA_ACK_IRQ(dev, dma);
+ BUG_ON(skb == NULL);
+ dma_unmap_single(NULL, tx_dmabuf, tx_dmalen, DMA_TO_DEVICE);
+ dev->trans_start = jiffies;
+ dev_kfree_skb_irq(skb);
+ lp->current_tx_skb = NULL;
+ if (lp->pending_tx_skb != NULL)
+ smc911x_hardware_send_pkt(dev);
+ else {
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA,
+ "%s: No pending Tx packets. DMA disabled\n", dev->name);
+ spin_lock_irqsave(&lp->lock, flags);
+ lp->txdma_active = 0;
+ if (!lp->tx_throttle) {
+ netif_wake_queue(dev);
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA,
+ "%s: TX DMA irq completed\n", dev->name);
+}
+static void
+smc911x_rx_dma_irq(int dma, void *data, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *)data;
+ unsigned long ioaddr = dev->base_addr;
+ struct smc911x_local *lp = netdev_priv(dev);
+ struct sk_buff *skb = lp->current_rx_skb;
+ unsigned long flags;
+ unsigned int pkts;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+ DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, "%s: RX DMA irq handler\n", dev->name);
+ /* Clear the DMA interrupt sources */
+ SMC_DMA_ACK_IRQ(dev, dma);
+ dma_unmap_single(NULL, rx_dmabuf, rx_dmalen, DMA_FROM_DEVICE);
+ BUG_ON(skb == NULL);
+ lp->current_rx_skb = NULL;
+ PRINT_PKT(skb->data, skb->len);
+ dev->last_rx = jiffies;
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += skb->len;
+
+ spin_lock_irqsave(&lp->lock, flags);
+ pkts = (SMC_GET_RX_FIFO_INF() & RX_FIFO_INF_RXSUSED_) >> 16;
+ if (pkts != 0) {
+ smc911x_rcv(dev);
+ }else {
+ lp->rxdma_active = 0;
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+ DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA,
+ "%s: RX DMA irq completed. DMA RX FIFO PKTS %d\n",
+ dev->name, pkts);
+}
+#endif /* SMC_USE_DMA */
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void smc911x_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smc911x_interrupt(dev->irq, dev, NULL);
+ enable_irq(dev->irq);
+}
+#endif
+
+/* Our watchdog timed out. Called by the networking layer */
+static void smc911x_timeout(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ int status, mask;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ status = SMC_GET_INT();
+ mask = SMC_GET_INT_EN();
+ spin_unlock_irqrestore(&lp->lock, flags);
+ DBG(SMC_DEBUG_MISC, "%s: INT 0x%02x MASK 0x%02x \n",
+ dev->name, status, mask);
+
+ /* Dump the current TX FIFO contents and restart */
+ mask = SMC_GET_TX_CFG();
+ SMC_SET_TX_CFG(mask | TX_CFG_TXS_DUMP_ | TX_CFG_TXD_DUMP_);
+ /*
+ * Reconfiguring the PHY doesn't seem like a bad idea here, but
+ * smc911x_phy_configure() calls msleep() which calls schedule_timeout()
+ * which calls schedule(). Hence we use a work queue.
+ */
+ if (lp->phy_type != 0) {
+ if (schedule_work(&lp->phy_configure)) {
+ lp->work_pending = 1;
+ }
+ }
+
+ /* We can accept TX packets again */
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+}
+
+/*
+ * This routine will, depending on the values passed to it,
+ * either make it accept multicast packets, go into
+ * promiscuous mode (for TCPDUMP and cousins) or accept
+ * a select set of multicast packets
+ */
+static void smc911x_set_multicast_list(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int multicast_table[2];
+ unsigned int mcr, update_multicast = 0;
+ unsigned long flags;
+ /* table for flipping the order of 5 bits */
+ static const unsigned char invert5[] =
+ {0x00, 0x10, 0x08, 0x18, 0x04, 0x14, 0x0C, 0x1C,
+ 0x02, 0x12, 0x0A, 0x1A, 0x06, 0x16, 0x0E, 0x1E,
+ 0x01, 0x11, 0x09, 0x19, 0x05, 0x15, 0x0D, 0x1D,
+ 0x03, 0x13, 0x0B, 0x1B, 0x07, 0x17, 0x0F, 0x1F};
+
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_GET_MAC_CR(mcr);
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ if (dev->flags & IFF_PROMISC) {
+
+ DBG(SMC_DEBUG_MISC, "%s: RCR_PRMS\n", dev->name);
+ mcr |= MAC_CR_PRMS_;
+ }
+ /*
+ * Here, I am setting this to accept all multicast packets.
+ * I don't need to zero the multicast table, because the flag is
+ * checked before the table is
+ */
+ else if (dev->flags & IFF_ALLMULTI || dev->mc_count > 16) {
+ DBG(SMC_DEBUG_MISC, "%s: RCR_ALMUL\n", dev->name);
+ mcr |= MAC_CR_MCPAS_;
+ }
+
+ /*
+ * This sets the internal hardware table to filter out unwanted
+ * multicast packets before they take up memory.
+ *
+ * The SMC chip uses a hash table where the high 6 bits of the CRC of
+ * address are the offset into the table. If that bit is 1, then the
+ * multicast packet is accepted. Otherwise, it's dropped silently.
+ *
+ * To use the 6 bits as an offset into the table, the high 1 bit is
+ * the number of the 32 bit register, while the low 5 bits are the bit
+ * within that register.
+ */
+ else if (dev->mc_count) {
+ int i;
+ struct dev_mc_list *cur_addr;
+
+ /* Set the Hash perfec mode */
+ mcr |= MAC_CR_HPFILT_;
+
+ /* start with a table of all zeros: reject all */
+ memset(multicast_table, 0, sizeof(multicast_table));
+
+ cur_addr = dev->mc_list;
+ for (i = 0; i < dev->mc_count; i++, cur_addr = cur_addr->next) {
+ int position;
+
+ /* do we have a pointer here? */
+ if (!cur_addr)
+ break;
+ /* make sure this is a multicast address -
+ shouldn't this be a given if we have it here ? */
+ if (!(*cur_addr->dmi_addr & 1))
+ continue;
+
+ /* only use the low order bits */
+ position = crc32_le(~0, cur_addr->dmi_addr, 6) & 0x3f;
+
+ /* do some messy swapping to put the bit in the right spot */
+ multicast_table[invert5[position&0x1F]&0x1] |=
+ (1<<invert5[(position>>1)&0x1F]);
+ }
+
+ /* be sure I get rid of flags I might have set */
+ mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ /* now, the table can be loaded into the chipset */
+ update_multicast = 1;
+ } else {
+ DBG(SMC_DEBUG_MISC, "%s: ~(MAC_CR_PRMS_|MAC_CR_MCPAS_)\n",
+ dev->name);
+ mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ /*
+ * since I'm disabling all multicast entirely, I need to
+ * clear the multicast list
+ */
+ memset(multicast_table, 0, sizeof(multicast_table));
+ update_multicast = 1;
+ }
+
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_SET_MAC_CR(mcr);
+ if (update_multicast) {
+ DBG(SMC_DEBUG_MISC,
+ "%s: update mcast hash table 0x%08x 0x%08x\n",
+ dev->name, multicast_table[0], multicast_table[1]);
+ SMC_SET_HASHL(multicast_table[0]);
+ SMC_SET_HASHH(multicast_table[1]);
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+
+/*
+ * Open and Initialize the board
+ *
+ * Set up everything, reset the card, etc..
+ */
+static int
+smc911x_open(struct net_device *dev)
+{
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ /*
+ * Check that the address is valid. If its not, refuse
+ * to bring the device up. The user must specify an
+ * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
+ */
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ PRINTK("%s: no valid ethernet hw addr\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ /* reset the hardware */
+ smc911x_reset(dev);
+
+ /* Configure the PHY, initialize the link state */
+ smc911x_phy_configure(dev);
+
+ /* Turn on Tx + Rx */
+ smc911x_enable(dev);
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/*
+ * smc911x_close
+ *
+ * this makes the board clean up everything that it can
+ * and not talk to the outside world. Caused by
+ * an 'ifconfig ethX down'
+ */
+static int smc911x_close(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+
+ /* clear everything */
+ smc911x_shutdown(dev);
+
+ if (lp->phy_type != 0) {
+ /* We need to ensure that no calls to
+ * smc911x_phy_configure are pending.
+
+ * flush_scheduled_work() cannot be called because we
+ * are running with the netlink semaphore held (from
+ * devinet_ioctl()) and the pending work queue
+ * contains linkwatch_event() (scheduled by
+ * netif_carrier_off() above). linkwatch_event() also
+ * wants the netlink semaphore.
+ */
+ while (lp->work_pending)
+ schedule();
+ smc911x_phy_powerdown(dev, lp->mii.phy_id);
+ }
+
+ if (lp->pending_tx_skb) {
+ dev_kfree_skb(lp->pending_tx_skb);
+ lp->pending_tx_skb = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *smc911x_query_statistics(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+
+ return &lp->stats;
+}
+
+/*
+ * Ethtool support
+ */
+static int
+smc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ int ret, status;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irqsave(&lp->lock, flags);
+ ret = mii_ethtool_gset(&lp->mii, cmd);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ } else {
+ cmd->supported = SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_TP | SUPPORTED_AUI;
+
+ if (lp->ctl_rspeed == 10)
+ cmd->speed = SPEED_10;
+ else if (lp->ctl_rspeed == 100)
+ cmd->speed = SPEED_100;
+
+ cmd->autoneg = AUTONEG_DISABLE;
+ if (lp->mii.phy_id==1)
+ cmd->transceiver = XCVR_INTERNAL;
+ else
+ cmd->transceiver = XCVR_EXTERNAL;
+ cmd->port = 0;
+ SMC_GET_PHY_SPECIAL(lp->mii.phy_id, status);
+ cmd->duplex =
+ (status & (PHY_SPECIAL_SPD_10FULL_ | PHY_SPECIAL_SPD_100FULL_)) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int
+smc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int ret;
+ unsigned long flags;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irqsave(&lp->lock, flags);
+ ret = mii_ethtool_sset(&lp->mii, cmd);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ } else {
+ if (cmd->autoneg != AUTONEG_DISABLE ||
+ cmd->speed != SPEED_10 ||
+ (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) ||
+ (cmd->port != PORT_TP && cmd->port != PORT_AUI))
+ return -EINVAL;
+
+ lp->ctl_rfduplx = cmd->duplex == DUPLEX_FULL;
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void
+smc911x_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strncpy(info->driver, CARDNAME, sizeof(info->driver));
+ strncpy(info->version, version, sizeof(info->version));
+ strncpy(info->bus_info, dev->class_dev.dev->bus_id, sizeof(info->bus_info));
+}
+
+static int smc911x_ethtool_nwayreset(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int ret = -EINVAL;
+ unsigned long flags;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irqsave(&lp->lock, flags);
+ ret = mii_nway_restart(&lp->mii);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+
+ return ret;
+}
+
+static u32 smc911x_ethtool_getmsglevel(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ return lp->msg_enable;
+}
+
+static void smc911x_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ lp->msg_enable = level;
+}
+
+static int smc911x_ethtool_getregslen(struct net_device *dev)
+{
+ /* System regs + MAC regs + PHY regs */
+ return (((E2P_CMD - ID_REV)/4 + 1) +
+ (WUCSR - MAC_CR)+1 + 32) * sizeof(u32);
+}
+
+static void smc911x_ethtool_getregs(struct net_device *dev,
+ struct ethtool_regs* regs, void *buf)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long flags;
+ u32 reg,i,j=0;
+ u32 *data = (u32*)buf;
+
+ regs->version = lp->version;
+ for(i=ID_REV;i<=E2P_CMD;i+=4) {
+ data[j++] = SMC_inl(ioaddr,i);
+ }
+ for(i=MAC_CR;i<=WUCSR;i++) {
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_GET_MAC_CSR(i, reg);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ data[j++] = reg;
+ }
+ for(i=0;i<=31;i++) {
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_GET_MII(i, lp->mii.phy_id, reg);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ data[j++] = reg & 0xFFFF;
+ }
+}
+
+static int smc911x_ethtool_wait_eeprom_ready(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int timeout;
+ int e2p_cmd;
+
+ e2p_cmd = SMC_GET_E2P_CMD();
+ for(timeout=10;(e2p_cmd & E2P_CMD_EPC_BUSY_) && timeout; timeout--) {
+ if (e2p_cmd & E2P_CMD_EPC_TIMEOUT_) {
+ PRINTK("%s: %s timeout waiting for EEPROM to respond\n",
+ dev->name, __FUNCTION__);
+ return -EFAULT;
+ }
+ mdelay(1);
+ e2p_cmd = SMC_GET_E2P_CMD();
+ }
+ if (timeout == 0) {
+ PRINTK("%s: %s timeout waiting for EEPROM CMD not busy\n",
+ dev->name, __FUNCTION__);
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static inline int smc911x_ethtool_write_eeprom_cmd(struct net_device *dev,
+ int cmd, int addr)
+{
+ unsigned long ioaddr = dev->base_addr;
+ int ret;
+
+ if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0)
+ return ret;
+ SMC_SET_E2P_CMD(E2P_CMD_EPC_BUSY_ |
+ ((cmd) & (0x7<<28)) |
+ ((addr) & 0xFF));
+ return 0;
+}
+
+static inline int smc911x_ethtool_read_eeprom_byte(struct net_device *dev,
+ u8 *data)
+{
+ unsigned long ioaddr = dev->base_addr;
+ int ret;
+
+ if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0)
+ return ret;
+ *data = SMC_GET_E2P_DATA();
+ return 0;
+}
+
+static inline int smc911x_ethtool_write_eeprom_byte(struct net_device *dev,
+ u8 data)
+{
+ unsigned long ioaddr = dev->base_addr;
+ int ret;
+
+ if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0)
+ return ret;
+ SMC_SET_E2P_DATA(data);
+ return 0;
+}
+
+static int smc911x_ethtool_geteeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ u8 eebuf[SMC911X_EEPROM_LEN];
+ int i, ret;
+
+ for(i=0;i<SMC911X_EEPROM_LEN;i++) {
+ if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_READ_, i ))!=0)
+ return ret;
+ if ((ret=smc911x_ethtool_read_eeprom_byte(dev, &eebuf[i]))!=0)
+ return ret;
+ }
+ memcpy(data, eebuf+eeprom->offset, eeprom->len);
+ return 0;
+}
+
+static int smc911x_ethtool_seteeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ int i, ret;
+
+ /* Enable erase */
+ if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_EWEN_, 0 ))!=0)
+ return ret;
+ for(i=eeprom->offset;i<(eeprom->offset+eeprom->len);i++) {
+ /* erase byte */
+ if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_ERASE_, i ))!=0)
+ return ret;
+ /* write byte */
+ if ((ret=smc911x_ethtool_write_eeprom_byte(dev, *data))!=0)
+ return ret;
+ if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_WRITE_, i ))!=0)
+ return ret;
+ }
+ return 0;
+}
+
+static int smc911x_ethtool_geteeprom_len(struct net_device *dev)
+{
+ return SMC911X_EEPROM_LEN;
+}
+
+static struct ethtool_ops smc911x_ethtool_ops = {
+ .get_settings = smc911x_ethtool_getsettings,
+ .set_settings = smc911x_ethtool_setsettings,
+ .get_drvinfo = smc911x_ethtool_getdrvinfo,
+ .get_msglevel = smc911x_ethtool_getmsglevel,
+ .set_msglevel = smc911x_ethtool_setmsglevel,
+ .nway_reset = smc911x_ethtool_nwayreset,
+ .get_link = ethtool_op_get_link,
+ .get_regs_len = smc911x_ethtool_getregslen,
+ .get_regs = smc911x_ethtool_getregs,
+ .get_eeprom_len = smc911x_ethtool_geteeprom_len,
+ .get_eeprom = smc911x_ethtool_geteeprom,
+ .set_eeprom = smc911x_ethtool_seteeprom,
+};
+
+/*
+ * smc911x_findirq
+ *
+ * This routine has a simple purpose -- make the SMC chip generate an
+ * interrupt, so an auto-detect routine can detect it, and find the IRQ,
+ */
+static int __init smc911x_findirq(unsigned long ioaddr)
+{
+ int timeout = 20;
+ unsigned long cookie;
+
+ DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__);
+
+ cookie = probe_irq_on();
+
+ /*
+ * Force a SW interrupt
+ */
+
+ SMC_SET_INT_EN(INT_EN_SW_INT_EN_);
+
+ /*
+ * Wait until positive that the interrupt has been generated
+ */
+ do {
+ int int_status;
+ udelay(10);
+ int_status = SMC_GET_INT_EN();
+ if (int_status & INT_EN_SW_INT_EN_)
+ break; /* got the interrupt */
+ } while (--timeout);
+
+ /*
+ * there is really nothing that I can do here if timeout fails,
+ * as autoirq_report will return a 0 anyway, which is what I
+ * want in this case. Plus, the clean up is needed in both
+ * cases.
+ */
+
+ /* and disable all interrupts again */
+ SMC_SET_INT_EN(0);
+
+ /* and return what I found */
+ return probe_irq_off(cookie);
+}
+
+/*
+ * Function: smc911x_probe(unsigned long ioaddr)
+ *
+ * Purpose:
+ * Tests to see if a given ioaddr points to an SMC911x chip.
+ * Returns a 0 on success
+ *
+ * Algorithm:
+ * (1) see if the endian word is OK
+ * (1) see if I recognize the chip ID in the appropriate register
+ *
+ * Here I do typical initialization tasks.
+ *
+ * o Initialize the structure if needed
+ * o print out my vanity message if not done so already
+ * o print out what type of hardware is detected
+ * o print out the ethernet address
+ * o find the IRQ
+ * o set up my private data
+ * o configure the dev structure with my subroutines
+ * o actually GRAB the irq.
+ * o GRAB the region
+ */
+static int __init smc911x_probe(struct net_device *dev, unsigned long ioaddr)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int i, retval;
+ unsigned int val, chip_id, revision;
+ const char *version_string;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
+
+ /* First, see if the endian word is recognized */
+ val = SMC_GET_BYTE_TEST();
+ DBG(SMC_DEBUG_MISC, "%s: endian probe returned 0x%04x\n", CARDNAME, val);
+ if (val != 0x87654321) {
+ printk(KERN_ERR "Invalid chip endian 0x08%x\n",val);
+ retval = -ENODEV;
+ goto err_out;
+ }
+
+ /*
+ * check if the revision register is something that I
+ * recognize. These might need to be added to later,
+ * as future revisions could be added.
+ */
+ chip_id = SMC_GET_PN();
+ DBG(SMC_DEBUG_MISC, "%s: id probe returned 0x%04x\n", CARDNAME, chip_id);
+ for(i=0;chip_ids[i].id != 0; i++) {
+ if (chip_ids[i].id == chip_id) break;
+ }
+ if (!chip_ids[i].id) {
+ printk(KERN_ERR "Unknown chip ID %04x\n", chip_id);
+ retval = -ENODEV;
+ goto err_out;
+ }
+ version_string = chip_ids[i].name;
+
+ revision = SMC_GET_REV();
+ DBG(SMC_DEBUG_MISC, "%s: revision = 0x%04x\n", CARDNAME, revision);
+
+ /* At this point I'll assume that the chip is an SMC911x. */
+ DBG(SMC_DEBUG_MISC, "%s: Found a %s\n", CARDNAME, chip_ids[i].name);
+
+ /* Validate the TX FIFO size requested */
+ if ((tx_fifo_kb < 2) || (tx_fifo_kb > 14)) {
+ printk(KERN_ERR "Invalid TX FIFO size requested %d\n", tx_fifo_kb);
+ retval = -EINVAL;
+ goto err_out;
+ }
+
+ /* fill in some of the fields */
+ dev->base_addr = ioaddr;
+ lp->version = chip_ids[i].id;
+ lp->revision = revision;
+ lp->tx_fifo_kb = tx_fifo_kb;
+ /* Reverse calculate the RX FIFO size from the TX */
+ lp->tx_fifo_size=(lp->tx_fifo_kb<<10) - 512;
+ lp->rx_fifo_size= ((0x4000 - 512 - lp->tx_fifo_size) / 16) * 15;
+
+ /* Set the automatic flow control values */
+ switch(lp->tx_fifo_kb) {
+ /*
+ * AFC_HI is about ((Rx Data Fifo Size)*2/3)/64
+ * AFC_LO is AFC_HI/2
+ * BACK_DUR is about 5uS*(AFC_LO) rounded down
+ */
+ case 2:/* 13440 Rx Data Fifo Size */
+ lp->afc_cfg=0x008C46AF;break;
+ case 3:/* 12480 Rx Data Fifo Size */
+ lp->afc_cfg=0x0082419F;break;
+ case 4:/* 11520 Rx Data Fifo Size */
+ lp->afc_cfg=0x00783C9F;break;
+ case 5:/* 10560 Rx Data Fifo Size */
+ lp->afc_cfg=0x006E374F;break;
+ case 6:/* 9600 Rx Data Fifo Size */
+ lp->afc_cfg=0x0064328F;break;
+ case 7:/* 8640 Rx Data Fifo Size */
+ lp->afc_cfg=0x005A2D7F;break;
+ case 8:/* 7680 Rx Data Fifo Size */
+ lp->afc_cfg=0x0050287F;break;
+ case 9:/* 6720 Rx Data Fifo Size */
+ lp->afc_cfg=0x0046236F;break;
+ case 10:/* 5760 Rx Data Fifo Size */
+ lp->afc_cfg=0x003C1E6F;break;
+ case 11:/* 4800 Rx Data Fifo Size */
+ lp->afc_cfg=0x0032195F;break;
+ /*
+ * AFC_HI is ~1520 bytes less than RX Data Fifo Size
+ * AFC_LO is AFC_HI/2
+ * BACK_DUR is about 5uS*(AFC_LO) rounded down
+ */
+ case 12:/* 3840 Rx Data Fifo Size */
+ lp->afc_cfg=0x0024124F;break;
+ case 13:/* 2880 Rx Data Fifo Size */
+ lp->afc_cfg=0x0015073F;break;
+ case 14:/* 1920 Rx Data Fifo Size */
+ lp->afc_cfg=0x0006032F;break;
+ default:
+ PRINTK("%s: ERROR -- no AFC_CFG setting found",
+ dev->name);
+ break;
+ }
+
+ DBG(SMC_DEBUG_MISC | SMC_DEBUG_TX | SMC_DEBUG_RX,
+ "%s: tx_fifo %d rx_fifo %d afc_cfg 0x%08x\n", CARDNAME,
+ lp->tx_fifo_size, lp->rx_fifo_size, lp->afc_cfg);
+
+ spin_lock_init(&lp->lock);
+
+ /* Get the MAC address */
+ SMC_GET_MAC_ADDR(dev->dev_addr);
+
+ /* now, reset the chip, and put it into a known state */
+ smc911x_reset(dev);
+
+ /*
+ * If dev->irq is 0, then the device has to be banged on to see
+ * what the IRQ is.
+ *
+ * Specifying an IRQ is done with the assumption that the user knows
+ * what (s)he is doing. No checking is done!!!!
+ */
+ if (dev->irq < 1) {
+ int trials;
+
+ trials = 3;
+ while (trials--) {
+ dev->irq = smc911x_findirq(ioaddr);
+ if (dev->irq)
+ break;
+ /* kick the card and try again */
+ smc911x_reset(dev);
+ }
+ }
+ if (dev->irq == 0) {
+ printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n",
+ dev->name);
+ retval = -ENODEV;
+ goto err_out;
+ }
+ dev->irq = irq_canonicalize(dev->irq);
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(dev);
+
+ dev->open = smc911x_open;
+ dev->stop = smc911x_close;
+ dev->hard_start_xmit = smc911x_hard_start_xmit;
+ dev->tx_timeout = smc911x_timeout;
+ dev->watchdog_timeo = msecs_to_jiffies(watchdog);
+ dev->get_stats = smc911x_query_statistics;
+ dev->set_multicast_list = smc911x_set_multicast_list;
+ dev->ethtool_ops = &smc911x_ethtool_ops;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = smc911x_poll_controller;
+#endif
+
+ INIT_WORK(&lp->phy_configure, smc911x_phy_configure, dev);
+ lp->mii.phy_id_mask = 0x1f;
+ lp->mii.reg_num_mask = 0x1f;
+ lp->mii.force_media = 0;
+ lp->mii.full_duplex = 0;
+ lp->mii.dev = dev;
+ lp->mii.mdio_read = smc911x_phy_read;
+ lp->mii.mdio_write = smc911x_phy_write;
+
+ /*
+ * Locate the phy, if any.
+ */
+ smc911x_phy_detect(dev);
+
+ /* Set default parameters */
+ lp->msg_enable = NETIF_MSG_LINK;
+ lp->ctl_rfduplx = 1;
+ lp->ctl_rspeed = 100;
+
+ /* Grab the IRQ */
+ retval = request_irq(dev->irq, &smc911x_interrupt, SA_SHIRQ, dev->name, dev);
+ if (retval)
+ goto err_out;
+
+ set_irq_type(dev->irq, IRQT_FALLING);
+
+#ifdef SMC_USE_DMA
+ lp->rxdma = SMC_DMA_REQUEST(dev, smc911x_rx_dma_irq);
+ lp->txdma = SMC_DMA_REQUEST(dev, smc911x_tx_dma_irq);
+ lp->rxdma_active = 0;
+ lp->txdma_active = 0;
+ dev->dma = lp->rxdma;
+#endif
+
+ retval = register_netdev(dev);
+ if (retval == 0) {
+ /* now, print out the card info, in a short format.. */
+ printk("%s: %s (rev %d) at %#lx IRQ %d",
+ dev->name, version_string, lp->revision,
+ dev->base_addr, dev->irq);
+
+#ifdef SMC_USE_DMA
+ if (lp->rxdma != -1)
+ printk(" RXDMA %d ", lp->rxdma);
+
+ if (lp->txdma != -1)
+ printk("TXDMA %d", lp->txdma);
+#endif
+ printk("\n");
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ printk("%s: Invalid ethernet MAC address. Please "
+ "set using ifconfig\n", dev->name);
+ } else {
+ /* Print the Ethernet address */
+ printk("%s: Ethernet addr: ", dev->name);
+ for (i = 0; i < 5; i++)
+ printk("%2.2x:", dev->dev_addr[i]);
+ printk("%2.2x\n", dev->dev_addr[5]);
+ }
+
+ if (lp->phy_type == 0) {
+ PRINTK("%s: No PHY found\n", dev->name);
+ } else if ((lp->phy_type & ~0xff) == LAN911X_INTERNAL_PHY_ID) {
+ PRINTK("%s: LAN911x Internal PHY\n", dev->name);
+ } else {
+ PRINTK("%s: External PHY 0x%08x\n", dev->name, lp->phy_type);
+ }
+ }
+
+err_out:
+#ifdef SMC_USE_DMA
+ if (retval) {
+ if (lp->rxdma != -1) {
+ SMC_DMA_FREE(dev, lp->rxdma);
+ }
+ if (lp->txdma != -1) {
+ SMC_DMA_FREE(dev, lp->txdma);
+ }
+ }
+#endif
+ return retval;
+}
+
+/*
+ * smc911x_init(void)
+ *
+ * Output:
+ * 0 --> there is a device
+ * anything else, error
+ */
+static int smc911x_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *ndev;
+ struct resource *res;
+ unsigned int *addr;
+ int ret;
+
+ DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /*
+ * Request the regions.
+ */
+ if (!request_mem_region(res->start, SMC911X_IO_EXTENT, CARDNAME)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ndev = alloc_etherdev(sizeof(struct smc911x_local));
+ if (!ndev) {
+ printk("%s: could not allocate device.\n", CARDNAME);
+ ret = -ENOMEM;
+ goto release_1;
+ }
+ SET_MODULE_OWNER(ndev);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ ndev->dma = (unsigned char)-1;
+ ndev->irq = platform_get_irq(pdev, 0);
+
+ addr = ioremap(res->start, SMC911X_IO_EXTENT);
+ if (!addr) {
+ ret = -ENOMEM;
+ goto release_both;
+ }
+
+ platform_set_drvdata(pdev, ndev);
+ ret = smc911x_probe(ndev, (unsigned long)addr);
+ if (ret != 0) {
+ platform_set_drvdata(pdev, NULL);
+ iounmap(addr);
+release_both:
+ free_netdev(ndev);
+release_1:
+ release_mem_region(res->start, SMC911X_IO_EXTENT);
+out:
+ printk("%s: not found (%d).\n", CARDNAME, ret);
+ }
+#ifdef SMC_USE_DMA
+ else {
+ struct smc911x_local *lp = netdev_priv(ndev);
+ lp->physaddr = res->start;
+ lp->dev = &pdev->dev;
+ }
+#endif
+
+ return ret;
+}
+
+static int smc911x_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__);
+ platform_set_drvdata(pdev, NULL);
+
+ unregister_netdev(ndev);
+
+ free_irq(ndev->irq, ndev);
+
+#ifdef SMC_USE_DMA
+ {
+ struct smc911x_local *lp = netdev_priv(ndev);
+ if (lp->rxdma != -1) {
+ SMC_DMA_FREE(dev, lp->rxdma);
+ }
+ if (lp->txdma != -1) {
+ SMC_DMA_FREE(dev, lp->txdma);
+ }
+ }
+#endif
+ iounmap((void *)ndev->base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, SMC911X_IO_EXTENT);
+
+ free_netdev(ndev);
+ return 0;
+}
+
+static int smc911x_drv_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(dev);
+ unsigned long ioaddr = ndev->base_addr;
+
+ DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__);
+ if (ndev) {
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ smc911x_shutdown(ndev);
+#if POWER_DOWN
+ /* Set D2 - Energy detect only setting */
+ SMC_SET_PMT_CTRL(2<<12);
+#endif
+ }
+ }
+ return 0;
+}
+
+static int smc911x_drv_resume(struct platform_device *dev)
+{
+ struct net_device *ndev = platform_get_drvdata(dev);
+
+ DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__);
+ if (ndev) {
+ struct smc911x_local *lp = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ smc911x_reset(ndev);
+ smc911x_enable(ndev);
+ if (lp->phy_type != 0)
+ smc911x_phy_configure(ndev);
+ netif_device_attach(ndev);
+ }
+ }
+ return 0;
+}
+
+static struct platform_driver smc911x_driver = {
+ .probe = smc911x_drv_probe,
+ .remove = smc911x_drv_remove,
+ .suspend = smc911x_drv_suspend,
+ .resume = smc911x_drv_resume,
+ .driver = {
+ .name = CARDNAME,
+ },
+};
+
+static int __init smc911x_init(void)
+{
+ return platform_driver_register(&smc911x_driver);
+}
+
+static void __exit smc911x_cleanup(void)
+{
+ platform_driver_unregister(&smc911x_driver);
+}
+
+module_init(smc911x_init);
+module_exit(smc911x_cleanup);
diff --git a/drivers/net/smc911x.h b/drivers/net/smc911x.h
new file mode 100644
index 0000000..962a710
--- /dev/null
+++ b/drivers/net/smc911x.h
@@ -0,0 +1,835 @@
+/*------------------------------------------------------------------------
+ . smc911x.h - macros for SMSC's LAN911{5,6,7,8} single-chip Ethernet device.
+ .
+ . Copyright (C) 2005 Sensoria Corp.
+ . Derived from the unified SMC91x driver by Nicolas Pitre
+ .
+ . 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.
+ .
+ . 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
+ .
+ . Information contained in this file was obtained from the LAN9118
+ . manual from SMC. To get a copy, if you really want one, you can find
+ . information under www.smsc.com.
+ .
+ . Authors
+ . Dustin McIntire <dustin@sensoria.com>
+ .
+ ---------------------------------------------------------------------------*/
+#ifndef _SMC911X_H_
+#define _SMC911X_H_
+
+/*
+ * Use the DMA feature on PXA chips
+ */
+#ifdef CONFIG_ARCH_PXA
+ #define SMC_USE_PXA_DMA 1
+ #define SMC_USE_16BIT 0
+ #define SMC_USE_32BIT 1
+#endif
+
+
+/*
+ * Define the bus width specific IO macros
+ */
+
+#if SMC_USE_16BIT
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inl(a, r) ((SMC_inw(a, r) & 0xFFFF)+(SMC_inw(a+2, r)<<16))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_outl(v, a, r) \
+ do{ \
+ writel(v & 0xFFFF, (a) + (r)); \
+ writel(v >> 16, (a) + (r) + 2); \
+ } while (0)
+#define SMC_insl(a, r, p, l) readsw((short*)((a) + (r)), p, l*2)
+#define SMC_outsl(a, r, p, l) writesw((short*)((a) + (r)), p, l*2)
+
+#elif SMC_USE_32BIT
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inl(a, r) readl((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insl(a, r, p, l) readsl((int*)((a) + (r)), p, l)
+#define SMC_outsl(a, r, p, l) writesl((int*)((a) + (r)), p, l)
+
+#endif /* SMC_USE_16BIT */
+
+
+
+#if SMC_USE_PXA_DMA
+#define SMC_USE_DMA
+
+/*
+ * Define the request and free functions
+ * These are unfortunately architecture specific as no generic allocation
+ * mechanism exits
+ */
+#define SMC_DMA_REQUEST(dev, handler) \
+ pxa_request_dma(dev->name, DMA_PRIO_LOW, handler, dev)
+
+#define SMC_DMA_FREE(dev, dma) \
+ pxa_free_dma(dma)
+
+#define SMC_DMA_ACK_IRQ(dev, dma) \
+{ \
+ if (DCSR(dma) & DCSR_BUSERR) { \
+ printk("%s: DMA %d bus error!\n", dev->name, dma); \
+ } \
+ DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; \
+}
+
+/*
+ * Use a DMA for RX and TX packets.
+ */
+#include <linux/dma-mapping.h>
+#include <asm/dma.h>
+#include <asm/arch/pxa-regs.h>
+
+static dma_addr_t rx_dmabuf, tx_dmabuf;
+static int rx_dmalen, tx_dmalen;
+
+#ifdef SMC_insl
+#undef SMC_insl
+#define SMC_insl(a, r, p, l) \
+ smc_pxa_dma_insl(lp->dev, a, lp->physaddr, r, lp->rxdma, p, l)
+
+static inline void
+smc_pxa_dma_insl(struct device *dev, u_long ioaddr, u_long physaddr,
+ int reg, int dma, u_char *buf, int len)
+{
+ /* 64 bit alignment is required for memory to memory DMA */
+ if ((long)buf & 4) {
+ *((u32 *)buf) = SMC_inl(ioaddr, reg);
+ buf += 4;
+ len--;
+ }
+
+ len *= 4;
+ rx_dmabuf = dma_map_single(dev, buf, len, DMA_FROM_DEVICE);
+ rx_dmalen = len;
+ DCSR(dma) = DCSR_NODESC;
+ DTADR(dma) = rx_dmabuf;
+ DSADR(dma) = physaddr + reg;
+ DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
+ DCMD_WIDTH4 | DCMD_ENDIRQEN | (DCMD_LENGTH & rx_dmalen));
+ DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+}
+#endif
+
+#ifdef SMC_insw
+#undef SMC_insw
+#define SMC_insw(a, r, p, l) \
+ smc_pxa_dma_insw(lp->dev, a, lp->physaddr, r, lp->rxdma, p, l)
+
+static inline void
+smc_pxa_dma_insw(struct device *dev, u_long ioaddr, u_long physaddr,
+ int reg, int dma, u_char *buf, int len)
+{
+ /* 64 bit alignment is required for memory to memory DMA */
+ while ((long)buf & 6) {
+ *((u16 *)buf) = SMC_inw(ioaddr, reg);
+ buf += 2;
+ len--;
+ }
+
+ len *= 2;
+ rx_dmabuf = dma_map_single(dev, buf, len, DMA_FROM_DEVICE);
+ rx_dmalen = len;
+ DCSR(dma) = DCSR_NODESC;
+ DTADR(dma) = rx_dmabuf;
+ DSADR(dma) = physaddr + reg;
+ DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
+ DCMD_WIDTH2 | DCMD_ENDIRQEN | (DCMD_LENGTH & rx_dmalen));
+ DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+}
+#endif
+
+#ifdef SMC_outsl
+#undef SMC_outsl
+#define SMC_outsl(a, r, p, l) \
+ smc_pxa_dma_outsl(lp->dev, a, lp->physaddr, r, lp->txdma, p, l)
+
+static inline void
+smc_pxa_dma_outsl(struct device *dev, u_long ioaddr, u_long physaddr,
+ int reg, int dma, u_char *buf, int len)
+{
+ /* 64 bit alignment is required for memory to memory DMA */
+ if ((long)buf & 4) {
+ SMC_outl(*((u32 *)buf), ioaddr, reg);
+ buf += 4;
+ len--;
+ }
+
+ len *= 4;
+ tx_dmabuf = dma_map_single(dev, buf, len, DMA_TO_DEVICE);
+ tx_dmalen = len;
+ DCSR(dma) = DCSR_NODESC;
+ DSADR(dma) = tx_dmabuf;
+ DTADR(dma) = physaddr + reg;
+ DCMD(dma) = (DCMD_INCSRCADDR | DCMD_BURST32 |
+ DCMD_WIDTH4 | DCMD_ENDIRQEN | (DCMD_LENGTH & tx_dmalen));
+ DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+}
+#endif
+
+#ifdef SMC_outsw
+#undef SMC_outsw
+#define SMC_outsw(a, r, p, l) \
+ smc_pxa_dma_outsw(lp->dev, a, lp->physaddr, r, lp->txdma, p, l)
+
+static inline void
+smc_pxa_dma_outsw(struct device *dev, u_long ioaddr, u_long physaddr,
+ int reg, int dma, u_char *buf, int len)
+{
+ /* 64 bit alignment is required for memory to memory DMA */
+ while ((long)buf & 6) {
+ SMC_outw(*((u16 *)buf), ioaddr, reg);
+ buf += 2;
+ len--;
+ }
+
+ len *= 2;
+ tx_dmabuf = dma_map_single(dev, buf, len, DMA_TO_DEVICE);
+ tx_dmalen = len;
+ DCSR(dma) = DCSR_NODESC;
+ DSADR(dma) = tx_dmabuf;
+ DTADR(dma) = physaddr + reg;
+ DCMD(dma) = (DCMD_INCSRCADDR | DCMD_BURST32 |
+ DCMD_WIDTH2 | DCMD_ENDIRQEN | (DCMD_LENGTH & tx_dmalen));
+ DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+}
+#endif
+
+#endif /* SMC_USE_PXA_DMA */
+
+
+/* Chip Parameters and Register Definitions */
+
+#define SMC911X_TX_FIFO_LOW_THRESHOLD (1536*2)
+
+#define SMC911X_IO_EXTENT 0x100
+
+#define SMC911X_EEPROM_LEN 7
+
+/* Below are the register offsets and bit definitions
+ * of the Lan911x memory space
+ */
+#define RX_DATA_FIFO (0x00)
+
+#define TX_DATA_FIFO (0x20)
+#define TX_CMD_A_INT_ON_COMP_ (0x80000000)
+#define TX_CMD_A_INT_BUF_END_ALGN_ (0x03000000)
+#define TX_CMD_A_INT_4_BYTE_ALGN_ (0x00000000)
+#define TX_CMD_A_INT_16_BYTE_ALGN_ (0x01000000)
+#define TX_CMD_A_INT_32_BYTE_ALGN_ (0x02000000)
+#define TX_CMD_A_INT_DATA_OFFSET_ (0x001F0000)
+#define TX_CMD_A_INT_FIRST_SEG_ (0x00002000)
+#define TX_CMD_A_INT_LAST_SEG_ (0x00001000)
+#define TX_CMD_A_BUF_SIZE_ (0x000007FF)
+#define TX_CMD_B_PKT_TAG_ (0xFFFF0000)
+#define TX_CMD_B_ADD_CRC_DISABLE_ (0x00002000)
+#define TX_CMD_B_DISABLE_PADDING_ (0x00001000)
+#define TX_CMD_B_PKT_BYTE_LENGTH_ (0x000007FF)
+
+#define RX_STATUS_FIFO (0x40)
+#define RX_STS_PKT_LEN_ (0x3FFF0000)
+#define RX_STS_ES_ (0x00008000)
+#define RX_STS_BCST_ (0x00002000)
+#define RX_STS_LEN_ERR_ (0x00001000)
+#define RX_STS_RUNT_ERR_ (0x00000800)
+#define RX_STS_MCAST_ (0x00000400)
+#define RX_STS_TOO_LONG_ (0x00000080)
+#define RX_STS_COLL_ (0x00000040)
+#define RX_STS_ETH_TYPE_ (0x00000020)
+#define RX_STS_WDOG_TMT_ (0x00000010)
+#define RX_STS_MII_ERR_ (0x00000008)
+#define RX_STS_DRIBBLING_ (0x00000004)
+#define RX_STS_CRC_ERR_ (0x00000002)
+#define RX_STATUS_FIFO_PEEK (0x44)
+#define TX_STATUS_FIFO (0x48)
+#define TX_STS_TAG_ (0xFFFF0000)
+#define TX_STS_ES_ (0x00008000)
+#define TX_STS_LOC_ (0x00000800)
+#define TX_STS_NO_CARR_ (0x00000400)
+#define TX_STS_LATE_COLL_ (0x00000200)
+#define TX_STS_MANY_COLL_ (0x00000100)
+#define TX_STS_COLL_CNT_ (0x00000078)
+#define TX_STS_MANY_DEFER_ (0x00000004)
+#define TX_STS_UNDERRUN_ (0x00000002)
+#define TX_STS_DEFERRED_ (0x00000001)
+#define TX_STATUS_FIFO_PEEK (0x4C)
+#define ID_REV (0x50)
+#define ID_REV_CHIP_ID_ (0xFFFF0000) /* RO */
+#define ID_REV_REV_ID_ (0x0000FFFF) /* RO */
+
+#define INT_CFG (0x54)
+#define INT_CFG_INT_DEAS_ (0xFF000000) /* R/W */
+#define INT_CFG_INT_DEAS_CLR_ (0x00004000)
+#define INT_CFG_INT_DEAS_STS_ (0x00002000)
+#define INT_CFG_IRQ_INT_ (0x00001000) /* RO */
+#define INT_CFG_IRQ_EN_ (0x00000100) /* R/W */
+#define INT_CFG_IRQ_POL_ (0x00000010) /* R/W Not Affected by SW Reset */
+#define INT_CFG_IRQ_TYPE_ (0x00000001) /* R/W Not Affected by SW Reset */
+
+#define INT_STS (0x58)
+#define INT_STS_SW_INT_ (0x80000000) /* R/WC */
+#define INT_STS_TXSTOP_INT_ (0x02000000) /* R/WC */
+#define INT_STS_RXSTOP_INT_ (0x01000000) /* R/WC */
+#define INT_STS_RXDFH_INT_ (0x00800000) /* R/WC */
+#define INT_STS_RXDF_INT_ (0x00400000) /* R/WC */
+#define INT_STS_TX_IOC_ (0x00200000) /* R/WC */
+#define INT_STS_RXD_INT_ (0x00100000) /* R/WC */
+#define INT_STS_GPT_INT_ (0x00080000) /* R/WC */
+#define INT_STS_PHY_INT_ (0x00040000) /* RO */
+#define INT_STS_PME_INT_ (0x00020000) /* R/WC */
+#define INT_STS_TXSO_ (0x00010000) /* R/WC */
+#define INT_STS_RWT_ (0x00008000) /* R/WC */
+#define INT_STS_RXE_ (0x00004000) /* R/WC */
+#define INT_STS_TXE_ (0x00002000) /* R/WC */
+//#define INT_STS_ERX_ (0x00001000) /* R/WC */
+#define INT_STS_TDFU_ (0x00000800) /* R/WC */
+#define INT_STS_TDFO_ (0x00000400) /* R/WC */
+#define INT_STS_TDFA_ (0x00000200) /* R/WC */
+#define INT_STS_TSFF_ (0x00000100) /* R/WC */
+#define INT_STS_TSFL_ (0x00000080) /* R/WC */
+//#define INT_STS_RXDF_ (0x00000040) /* R/WC */
+#define INT_STS_RDFO_ (0x00000040) /* R/WC */
+#define INT_STS_RDFL_ (0x00000020) /* R/WC */
+#define INT_STS_RSFF_ (0x00000010) /* R/WC */
+#define INT_STS_RSFL_ (0x00000008) /* R/WC */
+#define INT_STS_GPIO2_INT_ (0x00000004) /* R/WC */
+#define INT_STS_GPIO1_INT_ (0x00000002) /* R/WC */
+#define INT_STS_GPIO0_INT_ (0x00000001) /* R/WC */
+
+#define INT_EN (0x5C)
+#define INT_EN_SW_INT_EN_ (0x80000000) /* R/W */
+#define INT_EN_TXSTOP_INT_EN_ (0x02000000) /* R/W */
+#define INT_EN_RXSTOP_INT_EN_ (0x01000000) /* R/W */
+#define INT_EN_RXDFH_INT_EN_ (0x00800000) /* R/W */
+//#define INT_EN_RXDF_INT_EN_ (0x00400000) /* R/W */
+#define INT_EN_TIOC_INT_EN_ (0x00200000) /* R/W */
+#define INT_EN_RXD_INT_EN_ (0x00100000) /* R/W */
+#define INT_EN_GPT_INT_EN_ (0x00080000) /* R/W */
+#define INT_EN_PHY_INT_EN_ (0x00040000) /* R/W */
+#define INT_EN_PME_INT_EN_ (0x00020000) /* R/W */
+#define INT_EN_TXSO_EN_ (0x00010000) /* R/W */
+#define INT_EN_RWT_EN_ (0x00008000) /* R/W */
+#define INT_EN_RXE_EN_ (0x00004000) /* R/W */
+#define INT_EN_TXE_EN_ (0x00002000) /* R/W */
+//#define INT_EN_ERX_EN_ (0x00001000) /* R/W */
+#define INT_EN_TDFU_EN_ (0x00000800) /* R/W */
+#define INT_EN_TDFO_EN_ (0x00000400) /* R/W */
+#define INT_EN_TDFA_EN_ (0x00000200) /* R/W */
+#define INT_EN_TSFF_EN_ (0x00000100) /* R/W */
+#define INT_EN_TSFL_EN_ (0x00000080) /* R/W */
+//#define INT_EN_RXDF_EN_ (0x00000040) /* R/W */
+#define INT_EN_RDFO_EN_ (0x00000040) /* R/W */
+#define INT_EN_RDFL_EN_ (0x00000020) /* R/W */
+#define INT_EN_RSFF_EN_ (0x00000010) /* R/W */
+#define INT_EN_RSFL_EN_ (0x00000008) /* R/W */
+#define INT_EN_GPIO2_INT_ (0x00000004) /* R/W */
+#define INT_EN_GPIO1_INT_ (0x00000002) /* R/W */
+#define INT_EN_GPIO0_INT_ (0x00000001) /* R/W */
+
+#define BYTE_TEST (0x64)
+#define FIFO_INT (0x68)
+#define FIFO_INT_TX_AVAIL_LEVEL_ (0xFF000000) /* R/W */
+#define FIFO_INT_TX_STS_LEVEL_ (0x00FF0000) /* R/W */
+#define FIFO_INT_RX_AVAIL_LEVEL_ (0x0000FF00) /* R/W */
+#define FIFO_INT_RX_STS_LEVEL_ (0x000000FF) /* R/W */
+
+#define RX_CFG (0x6C)
+#define RX_CFG_RX_END_ALGN_ (0xC0000000) /* R/W */
+#define RX_CFG_RX_END_ALGN4_ (0x00000000) /* R/W */
+#define RX_CFG_RX_END_ALGN16_ (0x40000000) /* R/W */
+#define RX_CFG_RX_END_ALGN32_ (0x80000000) /* R/W */
+#define RX_CFG_RX_DMA_CNT_ (0x0FFF0000) /* R/W */
+#define RX_CFG_RX_DUMP_ (0x00008000) /* R/W */
+#define RX_CFG_RXDOFF_ (0x00001F00) /* R/W */
+//#define RX_CFG_RXBAD_ (0x00000001) /* R/W */
+
+#define TX_CFG (0x70)
+//#define TX_CFG_TX_DMA_LVL_ (0xE0000000) /* R/W */
+//#define TX_CFG_TX_DMA_CNT_ (0x0FFF0000) /* R/W Self Clearing */
+#define TX_CFG_TXS_DUMP_ (0x00008000) /* Self Clearing */
+#define TX_CFG_TXD_DUMP_ (0x00004000) /* Self Clearing */
+#define TX_CFG_TXSAO_ (0x00000004) /* R/W */
+#define TX_CFG_TX_ON_ (0x00000002) /* R/W */
+#define TX_CFG_STOP_TX_ (0x00000001) /* Self Clearing */
+
+#define HW_CFG (0x74)
+#define HW_CFG_TTM_ (0x00200000) /* R/W */
+#define HW_CFG_SF_ (0x00100000) /* R/W */
+#define HW_CFG_TX_FIF_SZ_ (0x000F0000) /* R/W */
+#define HW_CFG_TR_ (0x00003000) /* R/W */
+#define HW_CFG_PHY_CLK_SEL_ (0x00000060) /* R/W */
+#define HW_CFG_PHY_CLK_SEL_INT_PHY_ (0x00000000) /* R/W */
+#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ (0x00000020) /* R/W */
+#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ (0x00000040) /* R/W */
+#define HW_CFG_SMI_SEL_ (0x00000010) /* R/W */
+#define HW_CFG_EXT_PHY_DET_ (0x00000008) /* RO */
+#define HW_CFG_EXT_PHY_EN_ (0x00000004) /* R/W */
+#define HW_CFG_32_16_BIT_MODE_ (0x00000004) /* RO */
+#define HW_CFG_SRST_TO_ (0x00000002) /* RO */
+#define HW_CFG_SRST_ (0x00000001) /* Self Clearing */
+
+#define RX_DP_CTRL (0x78)
+#define RX_DP_CTRL_RX_FFWD_ (0x80000000) /* R/W */
+#define RX_DP_CTRL_FFWD_BUSY_ (0x80000000) /* RO */
+
+#define RX_FIFO_INF (0x7C)
+#define RX_FIFO_INF_RXSUSED_ (0x00FF0000) /* RO */
+#define RX_FIFO_INF_RXDUSED_ (0x0000FFFF) /* RO */
+
+#define TX_FIFO_INF (0x80)
+#define TX_FIFO_INF_TSUSED_ (0x00FF0000) /* RO */
+#define TX_FIFO_INF_TDFREE_ (0x0000FFFF) /* RO */
+
+#define PMT_CTRL (0x84)
+#define PMT_CTRL_PM_MODE_ (0x00003000) /* Self Clearing */
+#define PMT_CTRL_PHY_RST_ (0x00000400) /* Self Clearing */
+#define PMT_CTRL_WOL_EN_ (0x00000200) /* R/W */
+#define PMT_CTRL_ED_EN_ (0x00000100) /* R/W */
+#define PMT_CTRL_PME_TYPE_ (0x00000040) /* R/W Not Affected by SW Reset */
+#define PMT_CTRL_WUPS_ (0x00000030) /* R/WC */
+#define PMT_CTRL_WUPS_NOWAKE_ (0x00000000) /* R/WC */
+#define PMT_CTRL_WUPS_ED_ (0x00000010) /* R/WC */
+#define PMT_CTRL_WUPS_WOL_ (0x00000020) /* R/WC */
+#define PMT_CTRL_WUPS_MULTI_ (0x00000030) /* R/WC */
+#define PMT_CTRL_PME_IND_ (0x00000008) /* R/W */
+#define PMT_CTRL_PME_POL_ (0x00000004) /* R/W */
+#define PMT_CTRL_PME_EN_ (0x00000002) /* R/W Not Affected by SW Reset */
+#define PMT_CTRL_READY_ (0x00000001) /* RO */
+
+#define GPIO_CFG (0x88)
+#define GPIO_CFG_LED3_EN_ (0x40000000) /* R/W */
+#define GPIO_CFG_LED2_EN_ (0x20000000) /* R/W */
+#define GPIO_CFG_LED1_EN_ (0x10000000) /* R/W */
+#define GPIO_CFG_GPIO2_INT_POL_ (0x04000000) /* R/W */
+#define GPIO_CFG_GPIO1_INT_POL_ (0x02000000) /* R/W */
+#define GPIO_CFG_GPIO0_INT_POL_ (0x01000000) /* R/W */
+#define GPIO_CFG_EEPR_EN_ (0x00700000) /* R/W */
+#define GPIO_CFG_GPIOBUF2_ (0x00040000) /* R/W */
+#define GPIO_CFG_GPIOBUF1_ (0x00020000) /* R/W */
+#define GPIO_CFG_GPIOBUF0_ (0x00010000) /* R/W */
+#define GPIO_CFG_GPIODIR2_ (0x00000400) /* R/W */
+#define GPIO_CFG_GPIODIR1_ (0x00000200) /* R/W */
+#define GPIO_CFG_GPIODIR0_ (0x00000100) /* R/W */
+#define GPIO_CFG_GPIOD4_ (0x00000010) /* R/W */
+#define GPIO_CFG_GPIOD3_ (0x00000008) /* R/W */
+#define GPIO_CFG_GPIOD2_ (0x00000004) /* R/W */
+#define GPIO_CFG_GPIOD1_ (0x00000002) /* R/W */
+#define GPIO_CFG_GPIOD0_ (0x00000001) /* R/W */
+
+#define GPT_CFG (0x8C)
+#define GPT_CFG_TIMER_EN_ (0x20000000) /* R/W */
+#define GPT_CFG_GPT_LOAD_ (0x0000FFFF) /* R/W */
+
+#define GPT_CNT (0x90)
+#define GPT_CNT_GPT_CNT_ (0x0000FFFF) /* RO */
+
+#define ENDIAN (0x98)
+#define FREE_RUN (0x9C)
+#define RX_DROP (0xA0)
+#define MAC_CSR_CMD (0xA4)
+#define MAC_CSR_CMD_CSR_BUSY_ (0x80000000) /* Self Clearing */
+#define MAC_CSR_CMD_R_NOT_W_ (0x40000000) /* R/W */
+#define MAC_CSR_CMD_CSR_ADDR_ (0x000000FF) /* R/W */
+
+#define MAC_CSR_DATA (0xA8)
+#define AFC_CFG (0xAC)
+#define AFC_CFG_AFC_HI_ (0x00FF0000) /* R/W */
+#define AFC_CFG_AFC_LO_ (0x0000FF00) /* R/W */
+#define AFC_CFG_BACK_DUR_ (0x000000F0) /* R/W */
+#define AFC_CFG_FCMULT_ (0x00000008) /* R/W */
+#define AFC_CFG_FCBRD_ (0x00000004) /* R/W */
+#define AFC_CFG_FCADD_ (0x00000002) /* R/W */
+#define AFC_CFG_FCANY_ (0x00000001) /* R/W */
+
+#define E2P_CMD (0xB0)
+#define E2P_CMD_EPC_BUSY_ (0x80000000) /* Self Clearing */
+#define E2P_CMD_EPC_CMD_ (0x70000000) /* R/W */
+#define E2P_CMD_EPC_CMD_READ_ (0x00000000) /* R/W */
+#define E2P_CMD_EPC_CMD_EWDS_ (0x10000000) /* R/W */
+#define E2P_CMD_EPC_CMD_EWEN_ (0x20000000) /* R/W */
+#define E2P_CMD_EPC_CMD_WRITE_ (0x30000000) /* R/W */
+#define E2P_CMD_EPC_CMD_WRAL_ (0x40000000) /* R/W */
+#define E2P_CMD_EPC_CMD_ERASE_ (0x50000000) /* R/W */
+#define E2P_CMD_EPC_CMD_ERAL_ (0x60000000) /* R/W */
+#define E2P_CMD_EPC_CMD_RELOAD_ (0x70000000) /* R/W */
+#define E2P_CMD_EPC_TIMEOUT_ (0x00000200) /* RO */
+#define E2P_CMD_MAC_ADDR_LOADED_ (0x00000100) /* RO */
+#define E2P_CMD_EPC_ADDR_ (0x000000FF) /* R/W */
+
+#define E2P_DATA (0xB4)
+#define E2P_DATA_EEPROM_DATA_ (0x000000FF) /* R/W */
+/* end of LAN register offsets and bit definitions */
+
+/*
+ ****************************************************************************
+ ****************************************************************************
+ * MAC Control and Status Register (Indirect Address)
+ * Offset (through the MAC_CSR CMD and DATA port)
+ ****************************************************************************
+ ****************************************************************************
+ *
+ */
+#define MAC_CR (0x01) /* R/W */
+
+/* MAC_CR - MAC Control Register */
+#define MAC_CR_RXALL_ (0x80000000)
+// TODO: delete this bit? It is not described in the data sheet.
+#define MAC_CR_HBDIS_ (0x10000000)
+#define MAC_CR_RCVOWN_ (0x00800000)
+#define MAC_CR_LOOPBK_ (0x00200000)
+#define MAC_CR_FDPX_ (0x00100000)
+#define MAC_CR_MCPAS_ (0x00080000)
+#define MAC_CR_PRMS_ (0x00040000)
+#define MAC_CR_INVFILT_ (0x00020000)
+#define MAC_CR_PASSBAD_ (0x00010000)
+#define MAC_CR_HFILT_ (0x00008000)
+#define MAC_CR_HPFILT_ (0x00002000)
+#define MAC_CR_LCOLL_ (0x00001000)
+#define MAC_CR_BCAST_ (0x00000800)
+#define MAC_CR_DISRTY_ (0x00000400)
+#define MAC_CR_PADSTR_ (0x00000100)
+#define MAC_CR_BOLMT_MASK_ (0x000000C0)
+#define MAC_CR_DFCHK_ (0x00000020)
+#define MAC_CR_TXEN_ (0x00000008)
+#define MAC_CR_RXEN_ (0x00000004)
+
+#define ADDRH (0x02) /* R/W mask 0x0000FFFFUL */
+#define ADDRL (0x03) /* R/W mask 0xFFFFFFFFUL */
+#define HASHH (0x04) /* R/W */
+#define HASHL (0x05) /* R/W */
+
+#define MII_ACC (0x06) /* R/W */
+#define MII_ACC_PHY_ADDR_ (0x0000F800)
+#define MII_ACC_MIIRINDA_ (0x000007C0)
+#define MII_ACC_MII_WRITE_ (0x00000002)
+#define MII_ACC_MII_BUSY_ (0x00000001)
+
+#define MII_DATA (0x07) /* R/W mask 0x0000FFFFUL */
+
+#define FLOW (0x08) /* R/W */
+#define FLOW_FCPT_ (0xFFFF0000)
+#define FLOW_FCPASS_ (0x00000004)
+#define FLOW_FCEN_ (0x00000002)
+#define FLOW_FCBSY_ (0x00000001)
+
+#define VLAN1 (0x09) /* R/W mask 0x0000FFFFUL */
+#define VLAN1_VTI1_ (0x0000ffff)
+
+#define VLAN2 (0x0A) /* R/W mask 0x0000FFFFUL */
+#define VLAN2_VTI2_ (0x0000ffff)
+
+#define WUFF (0x0B) /* WO */
+
+#define WUCSR (0x0C) /* R/W */
+#define WUCSR_GUE_ (0x00000200)
+#define WUCSR_WUFR_ (0x00000040)
+#define WUCSR_MPR_ (0x00000020)
+#define WUCSR_WAKE_EN_ (0x00000004)
+#define WUCSR_MPEN_ (0x00000002)
+
+/*
+ ****************************************************************************
+ * Chip Specific MII Defines
+ ****************************************************************************
+ *
+ * Phy register offsets and bit definitions
+ *
+ */
+
+#define PHY_MODE_CTRL_STS ((u32)17) /* Mode Control/Status Register */
+//#define MODE_CTRL_STS_FASTRIP_ ((u16)0x4000)
+#define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000)
+//#define MODE_CTRL_STS_LOWSQEN_ ((u16)0x0800)
+//#define MODE_CTRL_STS_MDPREBP_ ((u16)0x0400)
+//#define MODE_CTRL_STS_FARLOOPBACK_ ((u16)0x0200)
+//#define MODE_CTRL_STS_FASTEST_ ((u16)0x0100)
+//#define MODE_CTRL_STS_REFCLKEN_ ((u16)0x0010)
+//#define MODE_CTRL_STS_PHYADBP_ ((u16)0x0008)
+//#define MODE_CTRL_STS_FORCE_G_LINK_ ((u16)0x0004)
+#define MODE_CTRL_STS_ENERGYON_ ((u16)0x0002)
+
+#define PHY_INT_SRC ((u32)29)
+#define PHY_INT_SRC_ENERGY_ON_ ((u16)0x0080)
+#define PHY_INT_SRC_ANEG_COMP_ ((u16)0x0040)
+#define PHY_INT_SRC_REMOTE_FAULT_ ((u16)0x0020)
+#define PHY_INT_SRC_LINK_DOWN_ ((u16)0x0010)
+#define PHY_INT_SRC_ANEG_LP_ACK_ ((u16)0x0008)
+#define PHY_INT_SRC_PAR_DET_FAULT_ ((u16)0x0004)
+#define PHY_INT_SRC_ANEG_PGRX_ ((u16)0x0002)
+
+#define PHY_INT_MASK ((u32)30)
+#define PHY_INT_MASK_ENERGY_ON_ ((u16)0x0080)
+#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040)
+#define PHY_INT_MASK_REMOTE_FAULT_ ((u16)0x0020)
+#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010)
+#define PHY_INT_MASK_ANEG_LP_ACK_ ((u16)0x0008)
+#define PHY_INT_MASK_PAR_DET_FAULT_ ((u16)0x0004)
+#define PHY_INT_MASK_ANEG_PGRX_ ((u16)0x0002)
+
+#define PHY_SPECIAL ((u32)31)
+#define PHY_SPECIAL_ANEG_DONE_ ((u16)0x1000)
+#define PHY_SPECIAL_RES_ ((u16)0x0040)
+#define PHY_SPECIAL_RES_MASK_ ((u16)0x0FE1)
+#define PHY_SPECIAL_SPD_ ((u16)0x001C)
+#define PHY_SPECIAL_SPD_10HALF_ ((u16)0x0004)
+#define PHY_SPECIAL_SPD_10FULL_ ((u16)0x0014)
+#define PHY_SPECIAL_SPD_100HALF_ ((u16)0x0008)
+#define PHY_SPECIAL_SPD_100FULL_ ((u16)0x0018)
+
+#define LAN911X_INTERNAL_PHY_ID (0x0007C000)
+
+/* Chip ID values */
+#define CHIP_9115 0x115
+#define CHIP_9116 0x116
+#define CHIP_9117 0x117
+#define CHIP_9118 0x118
+
+struct chip_id {
+ u16 id;
+ char *name;
+};
+
+static const struct chip_id chip_ids[] = {
+ { CHIP_9115, "LAN9115" },
+ { CHIP_9116, "LAN9116" },
+ { CHIP_9117, "LAN9117" },
+ { CHIP_9118, "LAN9118" },
+ { 0, NULL },
+};
+
+#define IS_REV_A(x) ((x & 0xFFFF)==0)
+
+/*
+ * Macros to abstract register access according to the data bus
+ * capabilities. Please use those and not the in/out primitives.
+ */
+/* FIFO read/write macros */
+#define SMC_PUSH_DATA(p, l) SMC_outsl( ioaddr, TX_DATA_FIFO, p, (l) >> 2 )
+#define SMC_PULL_DATA(p, l) SMC_insl ( ioaddr, RX_DATA_FIFO, p, (l) >> 2 )
+#define SMC_SET_TX_FIFO(x) SMC_outl( x, ioaddr, TX_DATA_FIFO )
+#define SMC_GET_RX_FIFO() SMC_inl( ioaddr, RX_DATA_FIFO )
+
+
+/* I/O mapped register read/write macros */
+#define SMC_GET_TX_STS_FIFO() SMC_inl( ioaddr, TX_STATUS_FIFO )
+#define SMC_GET_RX_STS_FIFO() SMC_inl( ioaddr, RX_STATUS_FIFO )
+#define SMC_GET_RX_STS_FIFO_PEEK() SMC_inl( ioaddr, RX_STATUS_FIFO_PEEK )
+#define SMC_GET_PN() (SMC_inl( ioaddr, ID_REV ) >> 16)
+#define SMC_GET_REV() (SMC_inl( ioaddr, ID_REV ) & 0xFFFF)
+#define SMC_GET_IRQ_CFG() SMC_inl( ioaddr, INT_CFG )
+#define SMC_SET_IRQ_CFG(x) SMC_outl( x, ioaddr, INT_CFG )
+#define SMC_GET_INT() SMC_inl( ioaddr, INT_STS )
+#define SMC_ACK_INT(x) SMC_outl( x, ioaddr, INT_STS )
+#define SMC_GET_INT_EN() SMC_inl( ioaddr, INT_EN )
+#define SMC_SET_INT_EN(x) SMC_outl( x, ioaddr, INT_EN )
+#define SMC_GET_BYTE_TEST() SMC_inl( ioaddr, BYTE_TEST )
+#define SMC_SET_BYTE_TEST(x) SMC_outl( x, ioaddr, BYTE_TEST )
+#define SMC_GET_FIFO_INT() SMC_inl( ioaddr, FIFO_INT )
+#define SMC_SET_FIFO_INT(x) SMC_outl( x, ioaddr, FIFO_INT )
+#define SMC_SET_FIFO_TDA(x) \
+ do { \
+ unsigned long __flags; \
+ int __mask; \
+ local_irq_save(__flags); \
+ __mask = SMC_GET_FIFO_INT() & ~(0xFF<<24); \
+ SMC_SET_FIFO_INT( __mask | (x)<<24 ); \
+ local_irq_restore(__flags); \
+ } while (0)
+#define SMC_SET_FIFO_TSL(x) \
+ do { \
+ unsigned long __flags; \
+ int __mask; \
+ local_irq_save(__flags); \
+ __mask = SMC_GET_FIFO_INT() & ~(0xFF<<16); \
+ SMC_SET_FIFO_INT( __mask | (((x) & 0xFF)<<16)); \
+ local_irq_restore(__flags); \
+ } while (0)
+#define SMC_SET_FIFO_RSA(x) \
+ do { \
+ unsigned long __flags; \
+ int __mask; \
+ local_irq_save(__flags); \
+ __mask = SMC_GET_FIFO_INT() & ~(0xFF<<8); \
+ SMC_SET_FIFO_INT( __mask | (((x) & 0xFF)<<8)); \
+ local_irq_restore(__flags); \
+ } while (0)
+#define SMC_SET_FIFO_RSL(x) \
+ do { \
+ unsigned long __flags; \
+ int __mask; \
+ local_irq_save(__flags); \
+ __mask = SMC_GET_FIFO_INT() & ~0xFF; \
+ SMC_SET_FIFO_INT( __mask | ((x) & 0xFF)); \
+ local_irq_restore(__flags); \
+ } while (0)
+#define SMC_GET_RX_CFG() SMC_inl( ioaddr, RX_CFG )
+#define SMC_SET_RX_CFG(x) SMC_outl( x, ioaddr, RX_CFG )
+#define SMC_GET_TX_CFG() SMC_inl( ioaddr, TX_CFG )
+#define SMC_SET_TX_CFG(x) SMC_outl( x, ioaddr, TX_CFG )
+#define SMC_GET_HW_CFG() SMC_inl( ioaddr, HW_CFG )
+#define SMC_SET_HW_CFG(x) SMC_outl( x, ioaddr, HW_CFG )
+#define SMC_GET_RX_DP_CTRL() SMC_inl( ioaddr, RX_DP_CTRL )
+#define SMC_SET_RX_DP_CTRL(x) SMC_outl( x, ioaddr, RX_DP_CTRL )
+#define SMC_GET_PMT_CTRL() SMC_inl( ioaddr, PMT_CTRL )
+#define SMC_SET_PMT_CTRL(x) SMC_outl( x, ioaddr, PMT_CTRL )
+#define SMC_GET_GPIO_CFG() SMC_inl( ioaddr, GPIO_CFG )
+#define SMC_SET_GPIO_CFG(x) SMC_outl( x, ioaddr, GPIO_CFG )
+#define SMC_GET_RX_FIFO_INF() SMC_inl( ioaddr, RX_FIFO_INF )
+#define SMC_SET_RX_FIFO_INF(x) SMC_outl( x, ioaddr, RX_FIFO_INF )
+#define SMC_GET_TX_FIFO_INF() SMC_inl( ioaddr, TX_FIFO_INF )
+#define SMC_SET_TX_FIFO_INF(x) SMC_outl( x, ioaddr, TX_FIFO_INF )
+#define SMC_GET_GPT_CFG() SMC_inl( ioaddr, GPT_CFG )
+#define SMC_SET_GPT_CFG(x) SMC_outl( x, ioaddr, GPT_CFG )
+#define SMC_GET_RX_DROP() SMC_inl( ioaddr, RX_DROP )
+#define SMC_SET_RX_DROP(x) SMC_outl( x, ioaddr, RX_DROP )
+#define SMC_GET_MAC_CMD() SMC_inl( ioaddr, MAC_CSR_CMD )
+#define SMC_SET_MAC_CMD(x) SMC_outl( x, ioaddr, MAC_CSR_CMD )
+#define SMC_GET_MAC_DATA() SMC_inl( ioaddr, MAC_CSR_DATA )
+#define SMC_SET_MAC_DATA(x) SMC_outl( x, ioaddr, MAC_CSR_DATA )
+#define SMC_GET_AFC_CFG() SMC_inl( ioaddr, AFC_CFG )
+#define SMC_SET_AFC_CFG(x) SMC_outl( x, ioaddr, AFC_CFG )
+#define SMC_GET_E2P_CMD() SMC_inl( ioaddr, E2P_CMD )
+#define SMC_SET_E2P_CMD(x) SMC_outl( x, ioaddr, E2P_CMD )
+#define SMC_GET_E2P_DATA() SMC_inl( ioaddr, E2P_DATA )
+#define SMC_SET_E2P_DATA(x) SMC_outl( x, ioaddr, E2P_DATA )
+
+/* MAC register read/write macros */
+#define SMC_GET_MAC_CSR(a,v) \
+ do { \
+ while (SMC_GET_MAC_CMD() & MAC_CSR_CMD_CSR_BUSY_); \
+ SMC_SET_MAC_CMD(MAC_CSR_CMD_CSR_BUSY_ | \
+ MAC_CSR_CMD_R_NOT_W_ | (a) ); \
+ while (SMC_GET_MAC_CMD() & MAC_CSR_CMD_CSR_BUSY_); \
+ v = SMC_GET_MAC_DATA(); \
+ } while (0)
+#define SMC_SET_MAC_CSR(a,v) \
+ do { \
+ while (SMC_GET_MAC_CMD() & MAC_CSR_CMD_CSR_BUSY_); \
+ SMC_SET_MAC_DATA(v); \
+ SMC_SET_MAC_CMD(MAC_CSR_CMD_CSR_BUSY_ | (a) ); \
+ while (SMC_GET_MAC_CMD() & MAC_CSR_CMD_CSR_BUSY_); \
+ } while (0)
+#define SMC_GET_MAC_CR(x) SMC_GET_MAC_CSR( MAC_CR, x )
+#define SMC_SET_MAC_CR(x) SMC_SET_MAC_CSR( MAC_CR, x )
+#define SMC_GET_ADDRH(x) SMC_GET_MAC_CSR( ADDRH, x )
+#define SMC_SET_ADDRH(x) SMC_SET_MAC_CSR( ADDRH, x )
+#define SMC_GET_ADDRL(x) SMC_GET_MAC_CSR( ADDRL, x )
+#define SMC_SET_ADDRL(x) SMC_SET_MAC_CSR( ADDRL, x )
+#define SMC_GET_HASHH(x) SMC_GET_MAC_CSR( HASHH, x )
+#define SMC_SET_HASHH(x) SMC_SET_MAC_CSR( HASHH, x )
+#define SMC_GET_HASHL(x) SMC_GET_MAC_CSR( HASHL, x )
+#define SMC_SET_HASHL(x) SMC_SET_MAC_CSR( HASHL, x )
+#define SMC_GET_MII_ACC(x) SMC_GET_MAC_CSR( MII_ACC, x )
+#define SMC_SET_MII_ACC(x) SMC_SET_MAC_CSR( MII_ACC, x )
+#define SMC_GET_MII_DATA(x) SMC_GET_MAC_CSR( MII_DATA, x )
+#define SMC_SET_MII_DATA(x) SMC_SET_MAC_CSR( MII_DATA, x )
+#define SMC_GET_FLOW(x) SMC_GET_MAC_CSR( FLOW, x )
+#define SMC_SET_FLOW(x) SMC_SET_MAC_CSR( FLOW, x )
+#define SMC_GET_VLAN1(x) SMC_GET_MAC_CSR( VLAN1, x )
+#define SMC_SET_VLAN1(x) SMC_SET_MAC_CSR( VLAN1, x )
+#define SMC_GET_VLAN2(x) SMC_GET_MAC_CSR( VLAN2, x )
+#define SMC_SET_VLAN2(x) SMC_SET_MAC_CSR( VLAN2, x )
+#define SMC_SET_WUFF(x) SMC_SET_MAC_CSR( WUFF, x )
+#define SMC_GET_WUCSR(x) SMC_GET_MAC_CSR( WUCSR, x )
+#define SMC_SET_WUCSR(x) SMC_SET_MAC_CSR( WUCSR, x )
+
+/* PHY register read/write macros */
+#define SMC_GET_MII(a,phy,v) \
+ do { \
+ u32 __v; \
+ do { \
+ SMC_GET_MII_ACC(__v); \
+ } while ( __v & MII_ACC_MII_BUSY_ ); \
+ SMC_SET_MII_ACC( ((phy)<<11) | ((a)<<6) | \
+ MII_ACC_MII_BUSY_); \
+ do { \
+ SMC_GET_MII_ACC(__v); \
+ } while ( __v & MII_ACC_MII_BUSY_ ); \
+ SMC_GET_MII_DATA(v); \
+ } while (0)
+#define SMC_SET_MII(a,phy,v) \
+ do { \
+ u32 __v; \
+ do { \
+ SMC_GET_MII_ACC(__v); \
+ } while ( __v & MII_ACC_MII_BUSY_ ); \
+ SMC_SET_MII_DATA(v); \
+ SMC_SET_MII_ACC( ((phy)<<11) | ((a)<<6) | \
+ MII_ACC_MII_BUSY_ | \
+ MII_ACC_MII_WRITE_ ); \
+ do { \
+ SMC_GET_MII_ACC(__v); \
+ } while ( __v & MII_ACC_MII_BUSY_ ); \
+ } while (0)
+#define SMC_GET_PHY_BMCR(phy,x) SMC_GET_MII( MII_BMCR, phy, x )
+#define SMC_SET_PHY_BMCR(phy,x) SMC_SET_MII( MII_BMCR, phy, x )
+#define SMC_GET_PHY_BMSR(phy,x) SMC_GET_MII( MII_BMSR, phy, x )
+#define SMC_GET_PHY_ID1(phy,x) SMC_GET_MII( MII_PHYSID1, phy, x )
+#define SMC_GET_PHY_ID2(phy,x) SMC_GET_MII( MII_PHYSID2, phy, x )
+#define SMC_GET_PHY_MII_ADV(phy,x) SMC_GET_MII( MII_ADVERTISE, phy, x )
+#define SMC_SET_PHY_MII_ADV(phy,x) SMC_SET_MII( MII_ADVERTISE, phy, x )
+#define SMC_GET_PHY_MII_LPA(phy,x) SMC_GET_MII( MII_LPA, phy, x )
+#define SMC_SET_PHY_MII_LPA(phy,x) SMC_SET_MII( MII_LPA, phy, x )
+#define SMC_GET_PHY_CTRL_STS(phy,x) SMC_GET_MII( PHY_MODE_CTRL_STS, phy, x )
+#define SMC_SET_PHY_CTRL_STS(phy,x) SMC_SET_MII( PHY_MODE_CTRL_STS, phy, x )
+#define SMC_GET_PHY_INT_SRC(phy,x) SMC_GET_MII( PHY_INT_SRC, phy, x )
+#define SMC_SET_PHY_INT_SRC(phy,x) SMC_SET_MII( PHY_INT_SRC, phy, x )
+#define SMC_GET_PHY_INT_MASK(phy,x) SMC_GET_MII( PHY_INT_MASK, phy, x )
+#define SMC_SET_PHY_INT_MASK(phy,x) SMC_SET_MII( PHY_INT_MASK, phy, x )
+#define SMC_GET_PHY_SPECIAL(phy,x) SMC_GET_MII( PHY_SPECIAL, phy, x )
+
+
+
+/* Misc read/write macros */
+
+#ifndef SMC_GET_MAC_ADDR
+#define SMC_GET_MAC_ADDR(addr) \
+ do { \
+ unsigned int __v; \
+ \
+ SMC_GET_MAC_CSR(ADDRL, __v); \
+ addr[0] = __v; addr[1] = __v >> 8; \
+ addr[2] = __v >> 16; addr[3] = __v >> 24; \
+ SMC_GET_MAC_CSR(ADDRH, __v); \
+ addr[4] = __v; addr[5] = __v >> 8; \
+ } while (0)
+#endif
+
+#define SMC_SET_MAC_ADDR(addr) \
+ do { \
+ SMC_SET_MAC_CSR(ADDRL, \
+ addr[0] | \
+ (addr[1] << 8) | \
+ (addr[2] << 16) | \
+ (addr[3] << 24)); \
+ SMC_SET_MAC_CSR(ADDRH, addr[4]|(addr[5] << 8));\
+ } while (0)
+
+
+#define SMC_WRITE_EEPROM_CMD(cmd, addr) \
+ do { \
+ while (SMC_GET_E2P_CMD() & MAC_CSR_CMD_CSR_BUSY_); \
+ SMC_SET_MAC_CMD(MAC_CSR_CMD_R_NOT_W_ | a ); \
+ while (SMC_GET_MAC_CMD() & MAC_CSR_CMD_CSR_BUSY_); \
+ } while (0)
+
+#endif /* _SMC911X_H_ */
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index e1be1af..f72a4f5 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -129,6 +129,24 @@
#define SMC_insb(a, r, p, l) readsb((a) + (r), p, (l))
#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l))
+#elif defined(CONFIG_MACH_LOGICPD_PXA270)
+
+#define SMC_CAN_USE_8BIT 0
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 0
+#define SMC_IO_SHIFT 0
+#define SMC_NOWAIT 1
+#define SMC_USE_PXA_DMA 1
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inl(a, r) readl((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
+
#elif defined(CONFIG_ARCH_INNOKOM) || \
defined(CONFIG_MACH_MAINSTONE) || \
defined(CONFIG_ARCH_PXA_IDP) || \
diff --git a/drivers/net/wan/pci200syn.c b/drivers/net/wan/pci200syn.c
index eba8e5c..f485a97 100644
--- a/drivers/net/wan/pci200syn.c
+++ b/drivers/net/wan/pci200syn.c
@@ -50,10 +50,6 @@
static int pci_clock_freq = 33000000;
#define CLOCK_BASE pci_clock_freq
-#define PCI_VENDOR_ID_GORAMO 0x10B5 /* uses PLX:9050 ID - this card */
-#define PCI_DEVICE_ID_PCI200SYN 0x9050 /* doesn't have its own ID */
-
-
/*
* PLX PCI9052 local configuration and shared runtime registers.
* This structure can be used to access 9052 registers (memory mapped).
@@ -262,7 +258,7 @@
int i;
card_t *card = pci_get_drvdata(pdev);
- for(i = 0; i < 2; i++)
+ for (i = 0; i < 2; i++)
if (card->ports[i].card) {
struct net_device *dev = port_to_dev(&card->ports[i]);
unregister_hdlc_device(dev);
@@ -385,6 +381,15 @@
" %u RX packets rings\n", ramsize / 1024, ramphys,
pdev->irq, card->tx_ring_buffers, card->rx_ring_buffers);
+ if (pdev->subsystem_device == PCI_DEVICE_ID_PLX_9050) {
+ printk(KERN_ERR "Detected PCI200SYN card with old "
+ "configuration data.\n");
+ printk(KERN_ERR "See <http://www.kernel.org/pub/"
+ "linux/utils/net/hdlc/pci200syn/> for update.\n");
+ printk(KERN_ERR "The card will stop working with"
+ " future versions of Linux if not updated.\n");
+ }
+
if (card->tx_ring_buffers < 1) {
printk(KERN_ERR "pci200syn: RAM test failed\n");
pci200_pci_remove_one(pdev);
@@ -396,7 +401,7 @@
writew(readw(p) | 0x0040, p);
/* Allocate IRQ */
- if(request_irq(pdev->irq, sca_intr, SA_SHIRQ, devname, card)) {
+ if (request_irq(pdev->irq, sca_intr, SA_SHIRQ, devname, card)) {
printk(KERN_WARNING "pci200syn: could not allocate IRQ%d.\n",
pdev->irq);
pci200_pci_remove_one(pdev);
@@ -406,7 +411,7 @@
sca_init(card, 0);
- for(i = 0; i < 2; i++) {
+ for (i = 0; i < 2; i++) {
port_t *port = &card->ports[i];
struct net_device *dev = port_to_dev(port);
hdlc_device *hdlc = dev_to_hdlc(dev);
@@ -425,7 +430,7 @@
hdlc->xmit = sca_xmit;
port->settings.clock_type = CLOCK_EXT;
port->card = card;
- if(register_hdlc_device(dev)) {
+ if (register_hdlc_device(dev)) {
printk(KERN_ERR "pci200syn: unable to register hdlc "
"device\n");
port->card = NULL;
@@ -445,8 +450,10 @@
static struct pci_device_id pci200_pci_tbl[] __devinitdata = {
- { PCI_VENDOR_ID_GORAMO, PCI_DEVICE_ID_PCI200SYN, PCI_ANY_ID,
- PCI_ANY_ID, 0, 0, 0 },
+ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX,
+ PCI_DEVICE_ID_PLX_9050, 0, 0, 0 },
+ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX,
+ PCI_DEVICE_ID_PLX_PCI200SYN, 0, 0, 0 },
{ 0, }
};
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index e0874cb..d7691c4 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -235,7 +235,35 @@
promiscuous mode via the Wireless Tool's Monitor mode. While in this
mode, no packets can be sent.
-config IPW_QOS
+config IPW2200_RADIOTAP
+ bool "Enable radiotap format 802.11 raw packet support"
+ depends on IPW2200_MONITOR
+
+config IPW2200_PROMISCUOUS
+ bool "Enable creation of a RF radiotap promiscuous interface"
+ depends on IPW2200_MONITOR
+ select IPW2200_RADIOTAP
+ ---help---
+ Enables the creation of a second interface prefixed 'rtap'.
+ This second interface will provide every received in radiotap
+ format.
+
+ This is useful for performing wireless network analysis while
+ maintaining an active association.
+
+ Example usage:
+
+ % modprobe ipw2200 rtap_iface=1
+ % ifconfig rtap0 up
+ % tethereal -i rtap0
+
+ If you do not specify 'rtap_iface=1' as a module parameter then
+ the rtap interface will not be created and you will need to turn
+ it on via sysfs:
+
+ % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface
+
+config IPW2200_QOS
bool "Enable QoS support"
depends on IPW2200 && EXPERIMENTAL
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index 00764dd..7f2dacf 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -47,6 +47,7 @@
#include <linux/ioport.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
+#include <net/ieee80211.h>
#include "airo.h"
@@ -467,6 +468,8 @@
#define RID_ECHOTEST_RESULTS 0xFF71
#define RID_BSSLISTFIRST 0xFF72
#define RID_BSSLISTNEXT 0xFF73
+#define RID_WPA_BSSLISTFIRST 0xFF74
+#define RID_WPA_BSSLISTNEXT 0xFF75
typedef struct {
u16 cmd;
@@ -739,6 +742,14 @@
u16 extSoftCap;
} CapabilityRid;
+
+/* Only present on firmware >= 5.30.17 */
+typedef struct {
+ u16 unknown[4];
+ u8 fixed[12]; /* WLAN management frame */
+ u8 iep[624];
+} BSSListRidExtra;
+
typedef struct {
u16 len;
u16 index; /* First is 0 and 0xffff means end of list */
@@ -767,6 +778,9 @@
} fh;
u16 dsChannel;
u16 atimWindow;
+
+ /* Only present on firmware >= 5.30.17 */
+ BSSListRidExtra extra;
} BSSListRid;
typedef struct {
@@ -1140,8 +1154,6 @@
char defindex; // Used with auto wep
struct proc_dir_entry *proc_entry;
spinlock_t aux_lock;
- unsigned long flags;
-#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */
#define FLAG_RADIO_OFF 0 /* User disabling of MAC */
#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */
#define FLAG_RADIO_MASK 0x03
@@ -1151,6 +1163,7 @@
#define FLAG_UPDATE_MULTI 5
#define FLAG_UPDATE_UNI 6
#define FLAG_802_11 7
+#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */
#define FLAG_PENDING_XMIT 9
#define FLAG_PENDING_XMIT11 10
#define FLAG_MPI 11
@@ -1158,17 +1171,19 @@
#define FLAG_COMMIT 13
#define FLAG_RESET 14
#define FLAG_FLASHING 15
-#define JOB_MASK 0x2ff0000
-#define JOB_DIE 16
-#define JOB_XMIT 17
-#define JOB_XMIT11 18
-#define JOB_STATS 19
-#define JOB_PROMISC 20
-#define JOB_MIC 21
-#define JOB_EVENT 22
-#define JOB_AUTOWEP 23
-#define JOB_WSTATS 24
-#define JOB_SCAN_RESULTS 25
+#define FLAG_WPA_CAPABLE 16
+ unsigned long flags;
+#define JOB_DIE 0
+#define JOB_XMIT 1
+#define JOB_XMIT11 2
+#define JOB_STATS 3
+#define JOB_PROMISC 4
+#define JOB_MIC 5
+#define JOB_EVENT 6
+#define JOB_AUTOWEP 7
+#define JOB_WSTATS 8
+#define JOB_SCAN_RESULTS 9
+ unsigned long jobs;
int (*bap_read)(struct airo_info*, u16 *pu16Dst, int bytelen,
int whichbap);
unsigned short *flash;
@@ -1208,6 +1223,11 @@
#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
char proc_name[IFNAMSIZ];
+ /* WPA-related stuff */
+ unsigned int bssListFirst;
+ unsigned int bssListNext;
+ unsigned int bssListRidLen;
+
struct list_head network_list;
struct list_head network_free_list;
BSSListElement *networks;
@@ -1264,7 +1284,7 @@
{
MICRid mic_rid;
- clear_bit(JOB_MIC, &ai->flags);
+ clear_bit(JOB_MIC, &ai->jobs);
PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
up(&ai->sem);
@@ -1705,24 +1725,24 @@
static int readBSSListRid(struct airo_info *ai, int first,
BSSListRid *list) {
int rc;
- Cmd cmd;
- Resp rsp;
+ Cmd cmd;
+ Resp rsp;
if (first == 1) {
- if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
- memset(&cmd, 0, sizeof(cmd));
- cmd.cmd=CMD_LISTBSS;
- if (down_interruptible(&ai->sem))
- return -ERESTARTSYS;
- issuecommand(ai, &cmd, &rsp);
- up(&ai->sem);
- /* Let the command take effect */
- ai->task = current;
- ssleep(3);
- ai->task = NULL;
- }
- rc = PC4500_readrid(ai, first ? RID_BSSLISTFIRST : RID_BSSLISTNEXT,
- list, sizeof(*list), 1);
+ if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd=CMD_LISTBSS;
+ if (down_interruptible(&ai->sem))
+ return -ERESTARTSYS;
+ issuecommand(ai, &cmd, &rsp);
+ up(&ai->sem);
+ /* Let the command take effect */
+ ai->task = current;
+ ssleep(3);
+ ai->task = NULL;
+ }
+ rc = PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext,
+ list, ai->bssListRidLen, 1);
list->len = le16_to_cpu(list->len);
list->index = le16_to_cpu(list->index);
@@ -2112,7 +2132,7 @@
int fid = priv->xmit.fid;
u32 *fids = priv->fids;
- clear_bit(JOB_XMIT, &priv->flags);
+ clear_bit(JOB_XMIT, &priv->jobs);
clear_bit(FLAG_PENDING_XMIT, &priv->flags);
status = transmit_802_3_packet (priv, fids[fid], skb->data);
up(&priv->sem);
@@ -2162,7 +2182,7 @@
if (down_trylock(&priv->sem) != 0) {
set_bit(FLAG_PENDING_XMIT, &priv->flags);
netif_stop_queue(dev);
- set_bit(JOB_XMIT, &priv->flags);
+ set_bit(JOB_XMIT, &priv->jobs);
wake_up_interruptible(&priv->thr_wait);
} else
airo_end_xmit(dev);
@@ -2177,7 +2197,7 @@
int fid = priv->xmit11.fid;
u32 *fids = priv->fids;
- clear_bit(JOB_XMIT11, &priv->flags);
+ clear_bit(JOB_XMIT11, &priv->jobs);
clear_bit(FLAG_PENDING_XMIT11, &priv->flags);
status = transmit_802_11_packet (priv, fids[fid], skb->data);
up(&priv->sem);
@@ -2233,7 +2253,7 @@
if (down_trylock(&priv->sem) != 0) {
set_bit(FLAG_PENDING_XMIT11, &priv->flags);
netif_stop_queue(dev);
- set_bit(JOB_XMIT11, &priv->flags);
+ set_bit(JOB_XMIT11, &priv->jobs);
wake_up_interruptible(&priv->thr_wait);
} else
airo_end_xmit11(dev);
@@ -2244,7 +2264,7 @@
StatsRid stats_rid;
u32 *vals = stats_rid.vals;
- clear_bit(JOB_STATS, &ai->flags);
+ clear_bit(JOB_STATS, &ai->jobs);
if (ai->power.event) {
up(&ai->sem);
return;
@@ -2272,10 +2292,10 @@
{
struct airo_info *local = dev->priv;
- if (!test_bit(JOB_STATS, &local->flags)) {
+ if (!test_bit(JOB_STATS, &local->jobs)) {
/* Get stats out of the card if available */
if (down_trylock(&local->sem) != 0) {
- set_bit(JOB_STATS, &local->flags);
+ set_bit(JOB_STATS, &local->jobs);
wake_up_interruptible(&local->thr_wait);
} else
airo_read_stats(local);
@@ -2290,7 +2310,7 @@
memset(&cmd, 0, sizeof(cmd));
cmd.cmd=CMD_SETMODE;
- clear_bit(JOB_PROMISC, &ai->flags);
+ clear_bit(JOB_PROMISC, &ai->jobs);
cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC;
issuecommand(ai, &cmd, &rsp);
up(&ai->sem);
@@ -2302,7 +2322,7 @@
if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
change_bit(FLAG_PROMISC, &ai->flags);
if (down_trylock(&ai->sem) != 0) {
- set_bit(JOB_PROMISC, &ai->flags);
+ set_bit(JOB_PROMISC, &ai->jobs);
wake_up_interruptible(&ai->thr_wait);
} else
airo_set_promisc(ai);
@@ -2380,7 +2400,7 @@
}
clear_bit(FLAG_REGISTERED, &ai->flags);
}
- set_bit(JOB_DIE, &ai->flags);
+ set_bit(JOB_DIE, &ai->jobs);
kill_proc(ai->thr_pid, SIGTERM, 1);
wait_for_completion(&ai->thr_exited);
@@ -2701,14 +2721,14 @@
return 0;
}
-#define MAX_NETWORK_COUNT 64
+#define AIRO_MAX_NETWORK_COUNT 64
static int airo_networks_allocate(struct airo_info *ai)
{
if (ai->networks)
return 0;
ai->networks =
- kzalloc(MAX_NETWORK_COUNT * sizeof(BSSListElement),
+ kzalloc(AIRO_MAX_NETWORK_COUNT * sizeof(BSSListElement),
GFP_KERNEL);
if (!ai->networks) {
airo_print_warn(ai->dev->name, "Out of memory allocating beacons");
@@ -2732,11 +2752,33 @@
INIT_LIST_HEAD(&ai->network_free_list);
INIT_LIST_HEAD(&ai->network_list);
- for (i = 0; i < MAX_NETWORK_COUNT; i++)
+ for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++)
list_add_tail(&ai->networks[i].list,
&ai->network_free_list);
}
+static int airo_test_wpa_capable(struct airo_info *ai)
+{
+ int status;
+ CapabilityRid cap_rid;
+ const char *name = ai->dev->name;
+
+ status = readCapabilityRid(ai, &cap_rid, 1);
+ if (status != SUCCESS) return 0;
+
+ /* Only firmware versions 5.30.17 or better can do WPA */
+ if ((cap_rid.softVer > 0x530)
+ || ((cap_rid.softVer == 0x530) && (cap_rid.softSubVer >= 0x17))) {
+ airo_print_info(name, "WPA is supported.");
+ return 1;
+ }
+
+ /* No WPA support */
+ airo_print_info(name, "WPA unsupported (only firmware versions 5.30.17"
+ " and greater support WPA. Detected %s)", cap_rid.prodVer);
+ return 0;
+}
+
static struct net_device *_init_airo_card( unsigned short irq, int port,
int is_pcmcia, struct pci_dev *pci,
struct device *dmdev )
@@ -2759,6 +2801,7 @@
ai = dev->priv;
ai->wifidev = NULL;
ai->flags = 0;
+ ai->jobs = 0;
ai->dev = dev;
if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
airo_print_dbg(dev->name, "Found an MPI350 card");
@@ -2838,6 +2881,18 @@
set_bit(FLAG_FLASHING, &ai->flags);
}
+ /* Test for WPA support */
+ if (airo_test_wpa_capable(ai)) {
+ set_bit(FLAG_WPA_CAPABLE, &ai->flags);
+ ai->bssListFirst = RID_WPA_BSSLISTFIRST;
+ ai->bssListNext = RID_WPA_BSSLISTNEXT;
+ ai->bssListRidLen = sizeof(BSSListRid);
+ } else {
+ ai->bssListFirst = RID_BSSLISTFIRST;
+ ai->bssListNext = RID_BSSLISTNEXT;
+ ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra);
+ }
+
rc = register_netdev(dev);
if (rc) {
airo_print_err(dev->name, "Couldn't register_netdev");
@@ -2875,7 +2930,7 @@
err_out_unlink:
del_airo_dev(dev);
err_out_thr:
- set_bit(JOB_DIE, &ai->flags);
+ set_bit(JOB_DIE, &ai->jobs);
kill_proc(ai->thr_pid, SIGTERM, 1);
wait_for_completion(&ai->thr_exited);
err_out_free:
@@ -2933,7 +2988,7 @@
union iwreq_data wrqu;
StatusRid status_rid;
- clear_bit(JOB_EVENT, &ai->flags);
+ clear_bit(JOB_EVENT, &ai->jobs);
PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
up(&ai->sem);
wrqu.data.length = 0;
@@ -2947,7 +3002,7 @@
static void airo_process_scan_results (struct airo_info *ai) {
union iwreq_data wrqu;
- BSSListRid BSSList;
+ BSSListRid bss;
int rc;
BSSListElement * loop_net;
BSSListElement * tmp_net;
@@ -2960,15 +3015,15 @@
}
/* Try to read the first entry of the scan result */
- rc = PC4500_readrid(ai, RID_BSSLISTFIRST, &BSSList, sizeof(BSSList), 0);
- if((rc) || (BSSList.index == 0xffff)) {
+ rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0);
+ if((rc) || (bss.index == 0xffff)) {
/* No scan results */
goto out;
}
/* Read and parse all entries */
tmp_net = NULL;
- while((!rc) && (BSSList.index != 0xffff)) {
+ while((!rc) && (bss.index != 0xffff)) {
/* Grab a network off the free list */
if (!list_empty(&ai->network_free_list)) {
tmp_net = list_entry(ai->network_free_list.next,
@@ -2977,19 +3032,19 @@
}
if (tmp_net != NULL) {
- memcpy(tmp_net, &BSSList, sizeof(tmp_net->bss));
+ memcpy(tmp_net, &bss, sizeof(tmp_net->bss));
list_add_tail(&tmp_net->list, &ai->network_list);
tmp_net = NULL;
}
/* Read next entry */
- rc = PC4500_readrid(ai, RID_BSSLISTNEXT,
- &BSSList, sizeof(BSSList), 0);
+ rc = PC4500_readrid(ai, ai->bssListNext,
+ &bss, ai->bssListRidLen, 0);
}
out:
ai->scan_timeout = 0;
- clear_bit(JOB_SCAN_RESULTS, &ai->flags);
+ clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
up(&ai->sem);
/* Send an empty event to user space.
@@ -3019,10 +3074,10 @@
/* make swsusp happy with our thread */
try_to_freeze();
- if (test_bit(JOB_DIE, &ai->flags))
+ if (test_bit(JOB_DIE, &ai->jobs))
break;
- if (ai->flags & JOB_MASK) {
+ if (ai->jobs) {
locked = down_interruptible(&ai->sem);
} else {
wait_queue_t wait;
@@ -3031,16 +3086,16 @@
add_wait_queue(&ai->thr_wait, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
- if (ai->flags & JOB_MASK)
+ if (ai->jobs)
break;
if (ai->expires || ai->scan_timeout) {
if (ai->scan_timeout &&
time_after_eq(jiffies,ai->scan_timeout)){
- set_bit(JOB_SCAN_RESULTS,&ai->flags);
+ set_bit(JOB_SCAN_RESULTS, &ai->jobs);
break;
} else if (ai->expires &&
time_after_eq(jiffies,ai->expires)){
- set_bit(JOB_AUTOWEP,&ai->flags);
+ set_bit(JOB_AUTOWEP, &ai->jobs);
break;
}
if (!signal_pending(current)) {
@@ -3069,7 +3124,7 @@
if (locked)
continue;
- if (test_bit(JOB_DIE, &ai->flags)) {
+ if (test_bit(JOB_DIE, &ai->jobs)) {
up(&ai->sem);
break;
}
@@ -3079,23 +3134,23 @@
continue;
}
- if (test_bit(JOB_XMIT, &ai->flags))
+ if (test_bit(JOB_XMIT, &ai->jobs))
airo_end_xmit(dev);
- else if (test_bit(JOB_XMIT11, &ai->flags))
+ else if (test_bit(JOB_XMIT11, &ai->jobs))
airo_end_xmit11(dev);
- else if (test_bit(JOB_STATS, &ai->flags))
+ else if (test_bit(JOB_STATS, &ai->jobs))
airo_read_stats(ai);
- else if (test_bit(JOB_WSTATS, &ai->flags))
+ else if (test_bit(JOB_WSTATS, &ai->jobs))
airo_read_wireless_stats(ai);
- else if (test_bit(JOB_PROMISC, &ai->flags))
+ else if (test_bit(JOB_PROMISC, &ai->jobs))
airo_set_promisc(ai);
- else if (test_bit(JOB_MIC, &ai->flags))
+ else if (test_bit(JOB_MIC, &ai->jobs))
micinit(ai);
- else if (test_bit(JOB_EVENT, &ai->flags))
+ else if (test_bit(JOB_EVENT, &ai->jobs))
airo_send_event(dev);
- else if (test_bit(JOB_AUTOWEP, &ai->flags))
+ else if (test_bit(JOB_AUTOWEP, &ai->jobs))
timer_func(dev);
- else if (test_bit(JOB_SCAN_RESULTS, &ai->flags))
+ else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs))
airo_process_scan_results(ai);
else /* Shouldn't get here, but we make sure to unlock */
up(&ai->sem);
@@ -3133,7 +3188,7 @@
if ( status & EV_MIC ) {
OUT4500( apriv, EVACK, EV_MIC );
if (test_bit(FLAG_MIC_CAPABLE, &apriv->flags)) {
- set_bit(JOB_MIC, &apriv->flags);
+ set_bit(JOB_MIC, &apriv->jobs);
wake_up_interruptible(&apriv->thr_wait);
}
}
@@ -3187,7 +3242,7 @@
set_bit(FLAG_UPDATE_MULTI, &apriv->flags);
if (down_trylock(&apriv->sem) != 0) {
- set_bit(JOB_EVENT, &apriv->flags);
+ set_bit(JOB_EVENT, &apriv->jobs);
wake_up_interruptible(&apriv->thr_wait);
} else
airo_send_event(dev);
@@ -5485,7 +5540,7 @@
up(&apriv->sem);
/* Schedule check to see if the change worked */
- clear_bit(JOB_AUTOWEP, &apriv->flags);
+ clear_bit(JOB_AUTOWEP, &apriv->jobs);
apriv->expires = RUN_AT(HZ*3);
}
@@ -6876,7 +6931,7 @@
}
range->num_txpower = i;
range->txpower_capa = IW_TXPOW_MWATT;
- range->we_version_source = 12;
+ range->we_version_source = 19;
range->we_version_compiled = WIRELESS_EXT;
range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
range->retry_flags = IW_RETRY_LIMIT;
@@ -7152,6 +7207,7 @@
u16 capabilities;
char * current_val; /* For rates */
int i;
+ char * buf;
/* First entry *MUST* be the AP MAC address */
iwe.cmd = SIOCGIWAP;
@@ -7238,8 +7294,69 @@
if((current_val - current_ev) > IW_EV_LCP_LEN)
current_ev = current_val;
- /* The other data in the scan result are not really
- * interesting, so for now drop it - Jean II */
+ /* Beacon interval */
+ buf = kmalloc(30, GFP_KERNEL);
+ if (buf) {
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, "bcn_int=%d", bss->beaconInterval);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf);
+ kfree(buf);
+ }
+
+ /* Put WPA/RSN Information Elements into the event stream */
+ if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
+ unsigned int num_null_ies = 0;
+ u16 length = sizeof (bss->extra.iep);
+ struct ieee80211_info_element *info_element =
+ (struct ieee80211_info_element *) &bss->extra.iep;
+
+ while ((length >= sizeof(*info_element)) && (num_null_ies < 2)) {
+ if (sizeof(*info_element) + info_element->len > length) {
+ /* Invalid element, don't continue parsing IE */
+ break;
+ }
+
+ switch (info_element->id) {
+ case MFIE_TYPE_SSID:
+ /* Two zero-length SSID elements
+ * mean we're done parsing elements */
+ if (!info_element->len)
+ num_null_ies++;
+ break;
+
+ case MFIE_TYPE_GENERIC:
+ if (info_element->len >= 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x50 &&
+ info_element->data[2] == 0xf2 &&
+ info_element->data[3] == 0x01) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = min(info_element->len + 2,
+ MAX_WPA_IE_LEN);
+ current_ev = iwe_stream_add_point(current_ev, end_buf,
+ &iwe, (char *) info_element);
+ }
+ break;
+
+ case MFIE_TYPE_RSN:
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = min(info_element->len + 2,
+ MAX_WPA_IE_LEN);
+ current_ev = iwe_stream_add_point(current_ev, end_buf,
+ &iwe, (char *) info_element);
+ break;
+
+ default:
+ break;
+ }
+
+ length -= sizeof(*info_element) + info_element->len;
+ info_element =
+ (struct ieee80211_info_element *)&info_element->
+ data[info_element->len];
+ }
+ }
return current_ev;
}
@@ -7521,7 +7638,7 @@
u32 *vals = stats_rid.vals;
/* Get stats out of the card */
- clear_bit(JOB_WSTATS, &local->flags);
+ clear_bit(JOB_WSTATS, &local->jobs);
if (local->power.event) {
up(&local->sem);
return;
@@ -7565,10 +7682,10 @@
{
struct airo_info *local = dev->priv;
- if (!test_bit(JOB_WSTATS, &local->flags)) {
+ if (!test_bit(JOB_WSTATS, &local->jobs)) {
/* Get stats out of the card if available */
if (down_trylock(&local->sem) != 0) {
- set_bit(JOB_WSTATS, &local->flags);
+ set_bit(JOB_WSTATS, &local->jobs);
wake_up_interruptible(&local->thr_wait);
} else
airo_read_wireless_stats(local);
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h
index 2e83083..e66fdb1 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx.h
+++ b/drivers/net/wireless/bcm43xx/bcm43xx.h
@@ -645,7 +645,6 @@
unsigned int irq;
void __iomem *mmio_addr;
- unsigned int mmio_len;
/* Do not use the lock directly. Use the bcm43xx_lock* helper
* functions, to be MMIO-safe. */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
index 35a4fcb..7497fb1 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
@@ -92,7 +92,7 @@
fappend("subsystem_vendor: 0x%04x subsystem_device: 0x%04x\n",
pci_dev->subsystem_vendor, pci_dev->subsystem_device);
fappend("IRQ: %d\n", bcm->irq);
- fappend("mmio_addr: 0x%p mmio_len: %u\n", bcm->mmio_addr, bcm->mmio_len);
+ fappend("mmio_addr: 0x%p\n", bcm->mmio_addr);
fappend("chip_id: 0x%04x chip_rev: 0x%02x\n", bcm->chip_id, bcm->chip_rev);
if ((bcm->core_80211[0].rev >= 3) && (bcm43xx_read32(bcm, 0x0158) & (1 << 16)))
fappend("Radio disabled by hardware!\n");
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
index 9a06e61..1985286 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
@@ -3288,8 +3288,7 @@
bcm43xx_chipset_detach(bcm);
/* Do _not_ access the chip, after it is detached. */
- iounmap(bcm->mmio_addr);
-
+ pci_iounmap(pci_dev, bcm->mmio_addr);
pci_release_regions(pci_dev);
pci_disable_device(pci_dev);
@@ -3379,40 +3378,26 @@
struct net_device *net_dev = bcm->net_dev;
int err;
int i;
- unsigned long mmio_start, mmio_flags, mmio_len;
u32 coremask;
err = pci_enable_device(pci_dev);
if (err) {
- printk(KERN_ERR PFX "unable to wake up pci device (%i)\n", err);
+ printk(KERN_ERR PFX "pci_enable_device() failed\n");
goto out;
}
- mmio_start = pci_resource_start(pci_dev, 0);
- mmio_flags = pci_resource_flags(pci_dev, 0);
- mmio_len = pci_resource_len(pci_dev, 0);
- if (!(mmio_flags & IORESOURCE_MEM)) {
- printk(KERN_ERR PFX
- "%s, region #0 not an MMIO resource, aborting\n",
- pci_name(pci_dev));
- err = -ENODEV;
- goto err_pci_disable;
- }
err = pci_request_regions(pci_dev, KBUILD_MODNAME);
if (err) {
- printk(KERN_ERR PFX
- "could not access PCI resources (%i)\n", err);
+ printk(KERN_ERR PFX "pci_request_regions() failed\n");
goto err_pci_disable;
}
/* enable PCI bus-mastering */
pci_set_master(pci_dev);
- bcm->mmio_addr = ioremap(mmio_start, mmio_len);
+ bcm->mmio_addr = pci_iomap(pci_dev, 0, ~0UL);
if (!bcm->mmio_addr) {
- printk(KERN_ERR PFX "%s: cannot remap MMIO, aborting\n",
- pci_name(pci_dev));
+ printk(KERN_ERR PFX "pci_iomap() failed\n");
err = -EIO;
goto err_pci_release;
}
- bcm->mmio_len = mmio_len;
net_dev->base_addr = (unsigned long)bcm->mmio_addr;
bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_VENDOR_ID,
@@ -3505,7 +3490,7 @@
err_chipset_detach:
bcm43xx_chipset_detach(bcm);
err_iounmap:
- iounmap(bcm->mmio_addr);
+ pci_iounmap(pci_dev, bcm->mmio_addr);
err_pci_release:
pci_release_regions(pci_dev);
err_pci_disable:
diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/hermes.c
index 346c6fe..2aa2f38 100644
--- a/drivers/net/wireless/hermes.c
+++ b/drivers/net/wireless/hermes.c
@@ -121,12 +121,6 @@
hw->iobase = address;
hw->reg_spacing = reg_spacing;
hw->inten = 0x0;
-
-#ifdef HERMES_DEBUG_BUFFER
- hw->dbufp = 0;
- memset(&hw->dbuf, 0xff, sizeof(hw->dbuf));
- memset(&hw->profile, 0, sizeof(hw->profile));
-#endif
}
int hermes_init(hermes_t *hw)
@@ -347,19 +341,6 @@
reg = hermes_read_reg(hw, oreg);
}
-#ifdef HERMES_DEBUG_BUFFER
- hw->profile[HERMES_BAP_BUSY_TIMEOUT - k]++;
-
- if (k < HERMES_BAP_BUSY_TIMEOUT) {
- struct hermes_debug_entry *e =
- &hw->dbuf[(hw->dbufp++) % HERMES_DEBUG_BUFSIZE];
- e->bap = bap;
- e->id = id;
- e->offset = offset;
- e->cycles = HERMES_BAP_BUSY_TIMEOUT - k;
- }
-#endif
-
if (reg & HERMES_OFFSET_BUSY)
return -ETIMEDOUT;
@@ -419,8 +400,7 @@
}
/* Write a block of data to the chip's buffer, via the
- * BAP. Synchronization/serialization is the caller's problem. len
- * must be even.
+ * BAP. Synchronization/serialization is the caller's problem.
*
* Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware
*/
@@ -430,7 +410,7 @@
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
int err = 0;
- if ( (len < 0) || (len % 2) )
+ if (len < 0)
return -EINVAL;
err = hermes_bap_seek(hw, bap, id, offset);
@@ -438,49 +418,12 @@
goto out;
/* Actually do the transfer */
- hermes_write_words(hw, dreg, buf, len/2);
+ hermes_write_bytes(hw, dreg, buf, len);
out:
return err;
}
-/* Write a block of data to the chip's buffer with padding if
- * neccessary, via the BAP. Synchronization/serialization is the
- * caller's problem. len must be even.
- *
- * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware
- */
-int hermes_bap_pwrite_pad(hermes_t *hw, int bap, const void *buf, unsigned data_len, int len,
- u16 id, u16 offset)
-{
- int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
- int err = 0;
-
- if (len < 0 || len % 2 || data_len > len)
- return -EINVAL;
-
- err = hermes_bap_seek(hw, bap, id, offset);
- if (err)
- goto out;
-
- /* Transfer all the complete words of data */
- hermes_write_words(hw, dreg, buf, data_len/2);
- /* If there is an odd byte left over pad and transfer it */
- if (data_len & 1) {
- u8 end[2];
- end[1] = 0;
- end[0] = ((unsigned char *)buf)[data_len - 1];
- hermes_write_words(hw, dreg, end, 1);
- data_len ++;
- }
- /* Now send zeros for the padding */
- if (data_len < len)
- hermes_clear_words(hw, dreg, (len - data_len) / 2);
- /* Complete */
- out:
- return err;
-}
-
/* Read a Length-Type-Value record from the card.
*
* If length is NULL, we ignore the length read from the card, and
@@ -553,7 +496,7 @@
count = length - 1;
- hermes_write_words(hw, dreg, value, count);
+ hermes_write_bytes(hw, dreg, value, count << 1);
err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE,
rid, NULL);
@@ -568,7 +511,6 @@
EXPORT_SYMBOL(hermes_bap_pread);
EXPORT_SYMBOL(hermes_bap_pwrite);
-EXPORT_SYMBOL(hermes_bap_pwrite_pad);
EXPORT_SYMBOL(hermes_read_ltv);
EXPORT_SYMBOL(hermes_write_ltv);
diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h
index 7644f72..8e3f0e3 100644
--- a/drivers/net/wireless/hermes.h
+++ b/drivers/net/wireless/hermes.h
@@ -328,16 +328,6 @@
u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN];
} __attribute__ ((packed));
-// #define HERMES_DEBUG_BUFFER 1
-#define HERMES_DEBUG_BUFSIZE 4096
-struct hermes_debug_entry {
- int bap;
- u16 id, offset;
- int cycles;
-};
-
-#ifdef __KERNEL__
-
/* Timeouts */
#define HERMES_BAP_BUSY_TIMEOUT (10000) /* In iterations of ~1us */
@@ -347,14 +337,7 @@
int reg_spacing;
#define HERMES_16BIT_REGSPACING 0
#define HERMES_32BIT_REGSPACING 1
-
u16 inten; /* Which interrupts should be enabled? */
-
-#ifdef HERMES_DEBUG_BUFFER
- struct hermes_debug_entry dbuf[HERMES_DEBUG_BUFSIZE];
- unsigned long dbufp;
- unsigned long profile[HERMES_BAP_BUSY_TIMEOUT+1];
-#endif
} hermes_t;
/* Register access convenience macros */
@@ -376,8 +359,6 @@
u16 id, u16 offset);
int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
u16 id, u16 offset);
-int hermes_bap_pwrite_pad(hermes_t *hw, int bap, const void *buf,
- unsigned data_len, int len, u16 id, u16 offset);
int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned buflen,
u16 *length, void *buf);
int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
@@ -425,10 +406,13 @@
ioread16_rep(hw->iobase + off, buf, count);
}
-static inline void hermes_write_words(struct hermes *hw, int off, const void *buf, unsigned count)
+static inline void hermes_write_bytes(struct hermes *hw, int off,
+ const char *buf, unsigned count)
{
off = off << hw->reg_spacing;
- iowrite16_rep(hw->iobase + off, buf, count);
+ iowrite16_rep(hw->iobase + off, buf, count >> 1);
+ if (unlikely(count & 1))
+ iowrite8(buf[count - 1], hw->iobase + off);
}
static inline void hermes_clear_words(struct hermes *hw, int off, unsigned count)
@@ -462,21 +446,4 @@
return HERMES_WRITE_RECORD(hw, bap, rid, &rec);
}
-#else /* ! __KERNEL__ */
-
-/* These are provided for the benefit of userspace drivers and testing programs
- which use ioperm() or iopl() */
-
-#define hermes_read_reg(base, off) (inw((base) + (off)))
-#define hermes_write_reg(base, off, val) (outw((val), (base) + (off)))
-
-#define hermes_read_regn(base, name) (hermes_read_reg((base), HERMES_##name))
-#define hermes_write_regn(base, name, val) (hermes_write_reg((base), HERMES_##name, (val)))
-
-/* Note that for the next two, the count is in 16-bit words, not bytes */
-#define hermes_read_data(base, off, buf, count) (insw((base) + (off), (buf), (count)))
-#define hermes_write_data(base, off, buf, count) (outsw((base) + (off), (buf), (count)))
-
-#endif /* ! __KERNEL__ */
-
#endif /* _HERMES_H */
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
index bca89cf..39f82f2 100644
--- a/drivers/net/wireless/ipw2200.c
+++ b/drivers/net/wireless/ipw2200.c
@@ -33,7 +33,44 @@
#include "ipw2200.h"
#include <linux/version.h>
-#define IPW2200_VERSION "git-1.1.1"
+
+#ifndef KBUILD_EXTMOD
+#define VK "k"
+#else
+#define VK
+#endif
+
+#ifdef CONFIG_IPW2200_DEBUG
+#define VD "d"
+#else
+#define VD
+#endif
+
+#ifdef CONFIG_IPW2200_MONITOR
+#define VM "m"
+#else
+#define VM
+#endif
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+#define VP "p"
+#else
+#define VP
+#endif
+
+#ifdef CONFIG_IPW2200_RADIOTAP
+#define VR "r"
+#else
+#define VR
+#endif
+
+#ifdef CONFIG_IPW2200_QOS
+#define VQ "q"
+#else
+#define VQ
+#endif
+
+#define IPW2200_VERSION "1.1.2" VK VD VM VP VR VQ
#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
#define DRV_VERSION IPW2200_VERSION
@@ -46,7 +83,9 @@
MODULE_LICENSE("GPL");
static int cmdlog = 0;
+#ifdef CONFIG_IPW2200_DEBUG
static int debug = 0;
+#endif
static int channel = 0;
static int mode = 0;
@@ -61,8 +100,14 @@
static const char ipw_modes[] = {
'a', 'b', 'g', '?'
};
+static int antenna = CFG_SYS_ANTENNA_BOTH;
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */
+#endif
+
+
+#ifdef CONFIG_IPW2200_QOS
static int qos_enable = 0;
static int qos_burst_enable = 0;
static int qos_no_ack_mask = 0;
@@ -126,7 +171,7 @@
*qos_param);
static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
*qos_param);
-#endif /* CONFIG_IPW_QOS */
+#endif /* CONFIG_IPW2200_QOS */
static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
static void ipw_remove_current_network(struct ipw_priv *priv);
@@ -1269,6 +1314,105 @@
static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL);
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static void ipw_prom_free(struct ipw_priv *priv);
+static int ipw_prom_alloc(struct ipw_priv *priv);
+static ssize_t store_rtap_iface(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ int rc = 0;
+
+ if (count < 1)
+ return -EINVAL;
+
+ switch (buf[0]) {
+ case '0':
+ if (!rtap_iface)
+ return count;
+
+ if (netif_running(priv->prom_net_dev)) {
+ IPW_WARNING("Interface is up. Cannot unregister.\n");
+ return count;
+ }
+
+ ipw_prom_free(priv);
+ rtap_iface = 0;
+ break;
+
+ case '1':
+ if (rtap_iface)
+ return count;
+
+ rc = ipw_prom_alloc(priv);
+ if (!rc)
+ rtap_iface = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (rc) {
+ IPW_ERROR("Failed to register promiscuous network "
+ "device (error %d).\n", rc);
+ }
+
+ return count;
+}
+
+static ssize_t show_rtap_iface(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ if (rtap_iface)
+ return sprintf(buf, "%s", priv->prom_net_dev->name);
+ else {
+ buf[0] = '-';
+ buf[1] = '1';
+ buf[2] = '\0';
+ return 3;
+ }
+}
+
+static DEVICE_ATTR(rtap_iface, S_IWUSR | S_IRUSR, show_rtap_iface,
+ store_rtap_iface);
+
+static ssize_t store_rtap_filter(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+
+ if (!priv->prom_priv) {
+ IPW_ERROR("Attempting to set filter without "
+ "rtap_iface enabled.\n");
+ return -EPERM;
+ }
+
+ priv->prom_priv->filter = simple_strtol(buf, NULL, 0);
+
+ IPW_DEBUG_INFO("Setting rtap filter to " BIT_FMT16 "\n",
+ BIT_ARG16(priv->prom_priv->filter));
+
+ return count;
+}
+
+static ssize_t show_rtap_filter(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ return sprintf(buf, "0x%04X",
+ priv->prom_priv ? priv->prom_priv->filter : 0);
+}
+
+static DEVICE_ATTR(rtap_filter, S_IWUSR | S_IRUSR, show_rtap_filter,
+ store_rtap_filter);
+#endif
+
static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
char *buf)
{
@@ -2025,16 +2169,11 @@
return ipw_send_cmd_simple(priv, IPW_CMD_HOST_COMPLETE);
}
-static int ipw_send_system_config(struct ipw_priv *priv,
- struct ipw_sys_config *config)
+static int ipw_send_system_config(struct ipw_priv *priv)
{
- if (!priv || !config) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG, sizeof(*config),
- config);
+ return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG,
+ sizeof(priv->sys_config),
+ &priv->sys_config);
}
static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
@@ -3104,10 +3243,10 @@
struct ipw_fw {
- u32 ver;
- u32 boot_size;
- u32 ucode_size;
- u32 fw_size;
+ __le32 ver;
+ __le32 boot_size;
+ __le32 ucode_size;
+ __le32 fw_size;
u8 data[0];
};
@@ -3131,8 +3270,8 @@
fw = (void *)(*raw)->data;
- if ((*raw)->size < sizeof(*fw) +
- fw->boot_size + fw->ucode_size + fw->fw_size) {
+ if ((*raw)->size < sizeof(*fw) + le32_to_cpu(fw->boot_size) +
+ le32_to_cpu(fw->ucode_size) + le32_to_cpu(fw->fw_size)) {
IPW_ERROR("%s is too small or corrupt (%zd)\n",
name, (*raw)->size);
return -EINVAL;
@@ -3237,8 +3376,9 @@
fw = (void *)raw->data;
boot_img = &fw->data[0];
- ucode_img = &fw->data[fw->boot_size];
- fw_img = &fw->data[fw->boot_size + fw->ucode_size];
+ ucode_img = &fw->data[le32_to_cpu(fw->boot_size)];
+ fw_img = &fw->data[le32_to_cpu(fw->boot_size) +
+ le32_to_cpu(fw->ucode_size)];
if (rc < 0)
goto error;
@@ -3272,7 +3412,7 @@
IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);
/* DMA the initial boot firmware into the device */
- rc = ipw_load_firmware(priv, boot_img, fw->boot_size);
+ rc = ipw_load_firmware(priv, boot_img, le32_to_cpu(fw->boot_size));
if (rc < 0) {
IPW_ERROR("Unable to load boot firmware: %d\n", rc);
goto error;
@@ -3294,7 +3434,7 @@
ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
/* DMA the ucode into the device */
- rc = ipw_load_ucode(priv, ucode_img, fw->ucode_size);
+ rc = ipw_load_ucode(priv, ucode_img, le32_to_cpu(fw->ucode_size));
if (rc < 0) {
IPW_ERROR("Unable to load ucode: %d\n", rc);
goto error;
@@ -3304,7 +3444,7 @@
ipw_stop_nic(priv);
/* DMA bss firmware into the device */
- rc = ipw_load_firmware(priv, fw_img, fw->fw_size);
+ rc = ipw_load_firmware(priv, fw_img, le32_to_cpu(fw->fw_size));
if (rc < 0) {
IPW_ERROR("Unable to load firmware: %d\n", rc);
goto error;
@@ -3700,7 +3840,17 @@
static void ipw_system_config(void *data)
{
struct ipw_priv *priv = data;
- ipw_send_system_config(priv, &priv->sys_config);
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
+ priv->sys_config.accept_all_data_frames = 1;
+ priv->sys_config.accept_non_directed_frames = 1;
+ priv->sys_config.accept_all_mgmt_bcpr = 1;
+ priv->sys_config.accept_all_mgmt_frames = 1;
+ }
+#endif
+
+ ipw_send_system_config(priv);
}
struct ipw_status_code {
@@ -3771,6 +3921,13 @@
memset(avg, 0, sizeof(*avg));
}
+#define DEPTH_RSSI 8
+#define DEPTH_NOISE 16
+static s16 exponential_average(s16 prev_avg, s16 val, u8 depth)
+{
+ return ((depth-1)*prev_avg + val)/depth;
+}
+
static void average_add(struct average *avg, s16 val)
{
avg->sum -= avg->entries[avg->pos];
@@ -3800,8 +3957,8 @@
priv->quality = 0;
average_init(&priv->average_missed_beacons);
- average_init(&priv->average_rssi);
- average_init(&priv->average_noise);
+ priv->exp_avg_rssi = -60;
+ priv->exp_avg_noise = -85 + 0x100;
priv->last_rate = 0;
priv->last_missed_beacons = 0;
@@ -4008,7 +4165,7 @@
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);
+ rssi = priv->exp_avg_rssi;
signal_quality =
(100 *
(priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
@@ -4185,7 +4342,7 @@
queue_work(priv->workqueue,
&priv->system_config);
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_ctl))
if ((priv->status & STATUS_AUTH) &&
@@ -4482,6 +4639,24 @@
&& priv->status & STATUS_ASSOCIATED)
queue_delayed_work(priv->workqueue,
&priv->request_scan, HZ);
+
+ /* Send an empty event to user space.
+ * We don't send the received data on the event because
+ * it would require us to do complex transcoding, and
+ * we want to minimise the work done in the irq handler
+ * Use a request to extract the data.
+ * Also, we generate this even for any scan, regardless
+ * on how the scan was initiated. User space can just
+ * sync on periodic scan to get fresh data...
+ * Jean II */
+ if (x->status == SCAN_COMPLETED_STATUS_COMPLETE) {
+ union iwreq_data wrqu;
+
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(priv->net_dev, SIOCGIWSCAN,
+ &wrqu, NULL);
+ }
break;
}
@@ -4577,11 +4752,10 @@
case HOST_NOTIFICATION_NOISE_STATS:{
if (notif->size == sizeof(u32)) {
- priv->last_noise =
- (u8) (le32_to_cpu(notif->u.noise.value) &
- 0xff);
- average_add(&priv->average_noise,
- priv->last_noise);
+ priv->exp_avg_noise =
+ exponential_average(priv->exp_avg_noise,
+ (u8) (le32_to_cpu(notif->u.noise.value) & 0xff),
+ DEPTH_NOISE);
break;
}
@@ -6170,8 +6344,6 @@
{
/* make sure WPA is enabled */
ipw_wpa_enable(priv, 1);
-
- ipw_disassociate(priv);
}
static int ipw_set_rsn_capa(struct ipw_priv *priv,
@@ -6365,6 +6537,7 @@
case IW_AUTH_WPA_ENABLED:
ret = ipw_wpa_enable(priv, param->value);
+ ipw_disassociate(priv);
break;
case IW_AUTH_RX_UNENCRYPTED_EAPOL:
@@ -6506,7 +6679,7 @@
return 0;
}
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
/* QoS */
/*
@@ -6853,61 +7026,55 @@
return from_priority_to_tx_queue[priority] - 1;
}
-/*
-* add QoS parameter to the TX command
-*/
-static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
- u16 priority,
- struct tfd_data *tfd, u8 unicast)
+static int ipw_is_qos_active(struct net_device *dev,
+ struct sk_buff *skb)
{
- int ret = 0;
- int tx_queue_id = 0;
+ struct ipw_priv *priv = ieee80211_priv(dev);
struct ieee80211_qos_data *qos_data = NULL;
int active, supported;
- unsigned long flags;
+ u8 *daddr = skb->data + ETH_ALEN;
+ int unicast = !is_multicast_ether_addr(daddr);
if (!(priv->status & STATUS_ASSOCIATED))
return 0;
qos_data = &priv->assoc_network->qos_data;
- spin_lock_irqsave(&priv->ieee->lock, flags);
-
if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
if (unicast == 0)
qos_data->active = 0;
else
qos_data->active = qos_data->supported;
}
-
active = qos_data->active;
supported = qos_data->supported;
-
- spin_unlock_irqrestore(&priv->ieee->lock, flags);
-
IPW_DEBUG_QOS("QoS %d network is QoS active %d supported %d "
"unicast %d\n",
priv->qos_data.qos_enable, active, supported, unicast);
- if (active && priv->qos_data.qos_enable) {
- ret = from_priority_to_tx_queue[priority];
- tx_queue_id = ret - 1;
- IPW_DEBUG_QOS("QoS packet priority is %d \n", priority);
- if (priority <= 7) {
- tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED;
- tfd->tfd.tfd_26.mchdr.qos_ctrl = priority;
- tfd->tfd.tfd_26.mchdr.frame_ctl |=
- IEEE80211_STYPE_QOS_DATA;
+ if (active && priv->qos_data.qos_enable)
+ return 1;
- if (priv->qos_data.qos_no_ack_mask &
- (1UL << tx_queue_id)) {
- tfd->tx_flags &= ~DCT_FLAG_ACK_REQD;
- tfd->tfd.tfd_26.mchdr.qos_ctrl |=
- CTRL_QOS_NO_ACK;
- }
- }
+ return 0;
+
+}
+/*
+* add QoS parameter to the TX command
+*/
+static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
+ u16 priority,
+ struct tfd_data *tfd)
+{
+ int tx_queue_id = 0;
+
+
+ tx_queue_id = from_priority_to_tx_queue[priority] - 1;
+ tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED;
+
+ if (priv->qos_data.qos_no_ack_mask & (1UL << tx_queue_id)) {
+ tfd->tx_flags &= ~DCT_FLAG_ACK_REQD;
+ tfd->tfd.tfd_26.mchdr.qos_ctrl |= CTRL_QOS_NO_ACK;
}
-
- return ret;
+ return 0;
}
/*
@@ -6977,7 +7144,7 @@
qos_param);
}
-#endif /* CONFIG_IPW_QOS */
+#endif /* CONFIG_IPW2200_QOS */
static int ipw_associate_network(struct ipw_priv *priv,
struct ieee80211_network *network,
@@ -7116,7 +7283,7 @@
else
priv->sys_config.answer_broadcast_ssid_probe = 0;
- err = ipw_send_system_config(priv, &priv->sys_config);
+ err = ipw_send_system_config(priv);
if (err) {
IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
return err;
@@ -7141,7 +7308,7 @@
priv->assoc_network = network;
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
ipw_qos_association(priv, network);
#endif
@@ -7415,7 +7582,7 @@
}
}
-#ifdef CONFIG_IEEE80211_RADIOTAP
+#ifdef CONFIG_IPW2200_RADIOTAP
static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
struct ipw_rx_mem_buffer *rxb,
struct ieee80211_rx_stats *stats)
@@ -7432,15 +7599,7 @@
/* Magic struct that slots into the radiotap header -- no reason
* to build this manually element by element, we can write it much
* more efficiently than we can parse it. ORDER MATTERS HERE */
- struct ipw_rt_hdr {
- struct ieee80211_radiotap_header rt_hdr;
- u8 rt_flags; /* radiotap packet flags */
- u8 rt_rate; /* rate in 500kb/s */
- u16 rt_channel; /* channel in mhz */
- u16 rt_chbitmask; /* channel bitfield */
- s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
- u8 rt_antenna; /* antenna number */
- } *ipw_rt;
+ struct ipw_rt_hdr *ipw_rt;
short len = le16_to_cpu(pkt->u.frame.length);
@@ -7494,9 +7653,11 @@
/* Big bitfield of all the fields we provide in radiotap */
ipw_rt->rt_hdr.it_present =
((1 << IEEE80211_RADIOTAP_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_TSFT) |
(1 << IEEE80211_RADIOTAP_RATE) |
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
(1 << IEEE80211_RADIOTAP_ANTENNA));
/* Zero the flags, we'll add to them as we go */
@@ -7582,6 +7743,217 @@
}
#endif
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+#define ieee80211_is_probe_response(fc) \
+ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && \
+ (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP )
+
+#define ieee80211_is_management(fc) \
+ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)
+
+#define ieee80211_is_control(fc) \
+ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL)
+
+#define ieee80211_is_data(fc) \
+ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
+
+#define ieee80211_is_assoc_request(fc) \
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ)
+
+#define ieee80211_is_reassoc_request(fc) \
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ)
+
+static void ipw_handle_promiscuous_rx(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;
+ struct ipw_rx_frame *frame = &pkt->u.frame;
+ struct ipw_rt_hdr *ipw_rt;
+
+ /* First cache any information we need before we overwrite
+ * the information provided in the skb from the hardware */
+ struct ieee80211_hdr *hdr;
+ u16 channel = frame->received_channel;
+ u8 phy_flags = frame->antennaAndPhy;
+ s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM;
+ s8 noise = frame->noise;
+ u8 rate = frame->rate;
+ short len = le16_to_cpu(pkt->u.frame.length);
+ u64 tsf = 0;
+ struct sk_buff *skb;
+ int hdr_only = 0;
+ u16 filter = priv->prom_priv->filter;
+
+ /* If the filter is set to not include Rx frames then return */
+ if (filter & IPW_PROM_NO_RX)
+ return;
+
+ /* We received data from the HW, so stop the watchdog */
+ priv->prom_net_dev->trans_start = jiffies;
+
+ if (unlikely((len + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) {
+ priv->prom_priv->ieee->stats.rx_errors++;
+ IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
+ return;
+ }
+
+ /* We only process data packets if the interface is open */
+ if (unlikely(!netif_running(priv->prom_net_dev))) {
+ priv->prom_priv->ieee->stats.rx_dropped++;
+ IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+ return;
+ }
+
+ /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
+ * that now */
+ if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
+ /* FIXME: Should alloc bigger skb instead */
+ priv->prom_priv->ieee->stats.rx_dropped++;
+ IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
+ return;
+ }
+
+ hdr = (void *)rxb->skb->data + IPW_RX_FRAME_SIZE;
+ if (ieee80211_is_management(hdr->frame_ctl)) {
+ if (filter & IPW_PROM_NO_MGMT)
+ return;
+ if (filter & IPW_PROM_MGMT_HEADER_ONLY)
+ hdr_only = 1;
+ } else if (ieee80211_is_control(hdr->frame_ctl)) {
+ if (filter & IPW_PROM_NO_CTL)
+ return;
+ if (filter & IPW_PROM_CTL_HEADER_ONLY)
+ hdr_only = 1;
+ } else if (ieee80211_is_data(hdr->frame_ctl)) {
+ if (filter & IPW_PROM_NO_DATA)
+ return;
+ if (filter & IPW_PROM_DATA_HEADER_ONLY)
+ hdr_only = 1;
+ }
+
+ /* Copy the SKB since this is for the promiscuous side */
+ skb = skb_copy(rxb->skb, GFP_ATOMIC);
+ if (skb == NULL) {
+ IPW_ERROR("skb_clone failed for promiscuous copy.\n");
+ return;
+ }
+
+ /* copy the frame data to write after where the radiotap header goes */
+ ipw_rt = (void *)skb->data;
+
+ if (hdr_only)
+ len = ieee80211_get_hdrlen(hdr->frame_ctl);
+
+ memcpy(ipw_rt->payload, hdr, len);
+
+ /* Zero the radiotap static buffer ... We only need to zero the bytes
+ * NOT part of our real header, saves a little time.
+ *
+ * No longer necessary since we fill in all our data. Purge before
+ * merging patch officially.
+ * memset(rxb->skb->data + sizeof(struct ipw_rt_hdr), 0,
+ * IEEE80211_RADIOTAP_HDRLEN - sizeof(struct ipw_rt_hdr));
+ */
+
+ ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+ ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
+ ipw_rt->rt_hdr.it_len = sizeof(*ipw_rt); /* total header+data */
+
+ /* Set the size of the skb to the size of the frame */
+ skb_put(skb, ipw_rt->rt_hdr.it_len + len);
+
+ /* Big bitfield of all the fields we provide in radiotap */
+ ipw_rt->rt_hdr.it_present =
+ ((1 << IEEE80211_RADIOTAP_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_TSFT) |
+ (1 << IEEE80211_RADIOTAP_RATE) |
+ (1 << IEEE80211_RADIOTAP_CHANNEL) |
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
+ (1 << IEEE80211_RADIOTAP_ANTENNA));
+
+ /* Zero the flags, we'll add to them as we go */
+ ipw_rt->rt_flags = 0;
+
+ ipw_rt->rt_tsf = tsf;
+
+ /* Convert to DBM */
+ ipw_rt->rt_dbmsignal = signal;
+ ipw_rt->rt_dbmnoise = noise;
+
+ /* Convert the channel data and set the flags */
+ ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(channel));
+ if (channel > 14) { /* 802.11a */
+ ipw_rt->rt_chbitmask =
+ cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
+ } else if (phy_flags & (1 << 5)) { /* 802.11b */
+ ipw_rt->rt_chbitmask =
+ cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
+ } else { /* 802.11g */
+ ipw_rt->rt_chbitmask =
+ (IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
+ }
+
+ /* set the rate in multiples of 500k/s */
+ switch (rate) {
+ case IPW_TX_RATE_1MB:
+ ipw_rt->rt_rate = 2;
+ break;
+ case IPW_TX_RATE_2MB:
+ ipw_rt->rt_rate = 4;
+ break;
+ case IPW_TX_RATE_5MB:
+ ipw_rt->rt_rate = 10;
+ break;
+ case IPW_TX_RATE_6MB:
+ ipw_rt->rt_rate = 12;
+ break;
+ case IPW_TX_RATE_9MB:
+ ipw_rt->rt_rate = 18;
+ break;
+ case IPW_TX_RATE_11MB:
+ ipw_rt->rt_rate = 22;
+ break;
+ case IPW_TX_RATE_12MB:
+ ipw_rt->rt_rate = 24;
+ break;
+ case IPW_TX_RATE_18MB:
+ ipw_rt->rt_rate = 36;
+ break;
+ case IPW_TX_RATE_24MB:
+ ipw_rt->rt_rate = 48;
+ break;
+ case IPW_TX_RATE_36MB:
+ ipw_rt->rt_rate = 72;
+ break;
+ case IPW_TX_RATE_48MB:
+ ipw_rt->rt_rate = 96;
+ break;
+ case IPW_TX_RATE_54MB:
+ ipw_rt->rt_rate = 108;
+ break;
+ default:
+ ipw_rt->rt_rate = 0;
+ break;
+ }
+
+ /* antenna number */
+ ipw_rt->rt_antenna = (phy_flags & 3);
+
+ /* set the preamble flag if we have it */
+ if (phy_flags & (1 << 6))
+ ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+
+ IPW_DEBUG_RX("Rx packet of %d bytes.\n", skb->len);
+
+ if (!ieee80211_rx(priv->prom_priv->ieee, skb, stats)) {
+ priv->prom_priv->ieee->stats.rx_errors++;
+ dev_kfree_skb_any(skb);
+ }
+}
+#endif
+
static int is_network_packet(struct ipw_priv *priv,
struct ieee80211_hdr_4addr *header)
{
@@ -7808,15 +8180,21 @@
priv->rx_packets++;
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ if (priv->prom_net_dev && netif_running(priv->prom_net_dev))
+ ipw_handle_promiscuous_rx(priv, rxb, &stats);
+#endif
+
#ifdef CONFIG_IPW2200_MONITOR
if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
-#ifdef CONFIG_IEEE80211_RADIOTAP
- ipw_handle_data_packet_monitor(priv,
- rxb,
- &stats);
+#ifdef CONFIG_IPW2200_RADIOTAP
+
+ ipw_handle_data_packet_monitor(priv,
+ rxb,
+ &stats);
#else
- ipw_handle_data_packet(priv, rxb,
- &stats);
+ ipw_handle_data_packet(priv, rxb,
+ &stats);
#endif
break;
}
@@ -7837,9 +8215,9 @@
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;
+ priv->exp_avg_rssi =
+ exponential_average(priv->exp_avg_rssi,
+ stats.rssi, DEPTH_RSSI);
}
IPW_DEBUG_RX("Frame: len=%u\n",
@@ -7982,10 +8360,10 @@
IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
/* TODO: Validate that provided channel is in range */
}
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
ipw_qos_init(priv, qos_enable, qos_burst_enable,
burst_duration_CCK, burst_duration_OFDM);
-#endif /* CONFIG_IPW_QOS */
+#endif /* CONFIG_IPW2200_QOS */
switch (mode) {
case 1:
@@ -7996,7 +8374,7 @@
#ifdef CONFIG_IPW2200_MONITOR
case 2:
priv->ieee->iw_mode = IW_MODE_MONITOR;
-#ifdef CONFIG_IEEE80211_RADIOTAP
+#ifdef CONFIG_IPW2200_RADIOTAP
priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
#else
priv->net_dev->type = ARPHRD_IEEE80211;
@@ -8251,7 +8629,7 @@
priv->net_dev->type = ARPHRD_ETHER;
if (wrqu->mode == IW_MODE_MONITOR)
-#ifdef CONFIG_IEEE80211_RADIOTAP
+#ifdef CONFIG_IPW2200_RADIOTAP
priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
#else
priv->net_dev->type = ARPHRD_IEEE80211;
@@ -8379,7 +8757,8 @@
/* 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(SIOCGIWAP) |
+ IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
range->event_capa[1] = IW_EVENT_CAPA_K_1;
range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
@@ -8734,6 +9113,7 @@
struct ipw_priv *priv = ieee80211_priv(dev);
mutex_lock(&priv->mutex);
wrqu->bitrate.value = priv->last_rate;
+ wrqu->bitrate.fixed = (priv->config & CFG_FIXED_RATE) ? 1 : 0;
mutex_unlock(&priv->mutex);
IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
return 0;
@@ -9351,7 +9731,7 @@
IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
if (enable) {
if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
-#ifdef CONFIG_IEEE80211_RADIOTAP
+#ifdef CONFIG_IPW2200_RADIOTAP
priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
#else
priv->net_dev->type = ARPHRD_IEEE80211;
@@ -9579,8 +9959,8 @@
}
wstats->qual.qual = priv->quality;
- wstats->qual.level = average_value(&priv->average_rssi);
- wstats->qual.noise = average_value(&priv->average_noise);
+ wstats->qual.level = priv->exp_avg_rssi;
+ wstats->qual.noise = priv->exp_avg_noise;
wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
IW_QUAL_NOISE_UPDATED | IW_QUAL_DBM;
@@ -9608,7 +9988,9 @@
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_SLOW_DIV;
+ if (antenna < CFG_SYS_ANTENNA_BOTH || antenna > CFG_SYS_ANTENNA_B)
+ antenna = CFG_SYS_ANTENNA_BOTH;
+ sys_config->antenna_diversity = antenna;
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;
@@ -9647,11 +10029,11 @@
static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
int pri)
{
- struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)
+ struct ieee80211_hdr_3addrqos *hdr = (struct ieee80211_hdr_3addrqos *)
txb->fragments[0]->data;
int i = 0;
struct tfd_frame *tfd;
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
int tx_id = ipw_get_tx_queue_number(priv, pri);
struct clx2_tx_queue *txq = &priv->txq[tx_id];
#else
@@ -9662,9 +10044,9 @@
u16 remaining_bytes;
int fc;
+ hdr_len = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
switch (priv->ieee->iw_mode) {
case IW_MODE_ADHOC:
- hdr_len = IEEE80211_3ADDR_LEN;
unicast = !is_multicast_ether_addr(hdr->addr1);
id = ipw_find_station(priv, hdr->addr1);
if (id == IPW_INVALID_STATION) {
@@ -9681,7 +10063,6 @@
case IW_MODE_INFRA:
default:
unicast = !is_multicast_ether_addr(hdr->addr3);
- hdr_len = IEEE80211_3ADDR_LEN;
id = 0;
break;
}
@@ -9759,9 +10140,10 @@
/* No hardware encryption */
tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP;
-#ifdef CONFIG_IPW_QOS
- ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data), unicast);
-#endif /* CONFIG_IPW_QOS */
+#ifdef CONFIG_IPW2200_QOS
+ if (fc & IEEE80211_STYPE_QOS_DATA)
+ ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data));
+#endif /* CONFIG_IPW2200_QOS */
/* payload */
tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2),
@@ -9841,12 +10223,12 @@
static int ipw_net_is_queue_full(struct net_device *dev, int pri)
{
struct ipw_priv *priv = ieee80211_priv(dev);
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
int tx_id = ipw_get_tx_queue_number(priv, pri);
struct clx2_tx_queue *txq = &priv->txq[tx_id];
#else
struct clx2_tx_queue *txq = &priv->txq[0];
-#endif /* CONFIG_IPW_QOS */
+#endif /* CONFIG_IPW2200_QOS */
if (ipw_queue_space(&txq->q) < txq->q.high_mark)
return 1;
@@ -9854,6 +10236,88 @@
return 0;
}
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static void ipw_handle_promiscuous_tx(struct ipw_priv *priv,
+ struct ieee80211_txb *txb)
+{
+ struct ieee80211_rx_stats dummystats;
+ struct ieee80211_hdr *hdr;
+ u8 n;
+ u16 filter = priv->prom_priv->filter;
+ int hdr_only = 0;
+
+ if (filter & IPW_PROM_NO_TX)
+ return;
+
+ memset(&dummystats, 0, sizeof(dummystats));
+
+ /* Filtering of fragment chains is done agains the first fragment */
+ hdr = (void *)txb->fragments[0]->data;
+ if (ieee80211_is_management(hdr->frame_ctl)) {
+ if (filter & IPW_PROM_NO_MGMT)
+ return;
+ if (filter & IPW_PROM_MGMT_HEADER_ONLY)
+ hdr_only = 1;
+ } else if (ieee80211_is_control(hdr->frame_ctl)) {
+ if (filter & IPW_PROM_NO_CTL)
+ return;
+ if (filter & IPW_PROM_CTL_HEADER_ONLY)
+ hdr_only = 1;
+ } else if (ieee80211_is_data(hdr->frame_ctl)) {
+ if (filter & IPW_PROM_NO_DATA)
+ return;
+ if (filter & IPW_PROM_DATA_HEADER_ONLY)
+ hdr_only = 1;
+ }
+
+ for(n=0; n<txb->nr_frags; ++n) {
+ struct sk_buff *src = txb->fragments[n];
+ struct sk_buff *dst;
+ struct ieee80211_radiotap_header *rt_hdr;
+ int len;
+
+ if (hdr_only) {
+ hdr = (void *)src->data;
+ len = ieee80211_get_hdrlen(hdr->frame_ctl);
+ } else
+ len = src->len;
+
+ dst = alloc_skb(
+ len + IEEE80211_RADIOTAP_HDRLEN, GFP_ATOMIC);
+ if (!dst) continue;
+
+ rt_hdr = (void *)skb_put(dst, sizeof(*rt_hdr));
+
+ rt_hdr->it_version = PKTHDR_RADIOTAP_VERSION;
+ rt_hdr->it_pad = 0;
+ rt_hdr->it_present = 0; /* after all, it's just an idea */
+ rt_hdr->it_present |= (1 << IEEE80211_RADIOTAP_CHANNEL);
+
+ *(u16*)skb_put(dst, sizeof(u16)) = cpu_to_le16(
+ ieee80211chan2mhz(priv->channel));
+ if (priv->channel > 14) /* 802.11a */
+ *(u16*)skb_put(dst, sizeof(u16)) =
+ cpu_to_le16(IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_5GHZ);
+ else if (priv->ieee->mode == IEEE_B) /* 802.11b */
+ *(u16*)skb_put(dst, sizeof(u16)) =
+ cpu_to_le16(IEEE80211_CHAN_CCK |
+ IEEE80211_CHAN_2GHZ);
+ else /* 802.11g */
+ *(u16*)skb_put(dst, sizeof(u16)) =
+ cpu_to_le16(IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_2GHZ);
+
+ rt_hdr->it_len = dst->len;
+
+ memcpy(skb_put(dst, len), src->data, len);
+
+ if (!ieee80211_rx(priv->prom_priv->ieee, dst, &dummystats))
+ dev_kfree_skb_any(dst);
+ }
+}
+#endif
+
static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
struct net_device *dev, int pri)
{
@@ -9871,6 +10335,11 @@
goto fail_unlock;
}
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ if (rtap_iface && netif_running(priv->prom_net_dev))
+ ipw_handle_promiscuous_tx(priv, txb);
+#endif
+
ret = ipw_tx_skb(priv, txb, pri);
if (ret == NETDEV_TX_OK)
__ipw_led_activity_on(priv);
@@ -10169,10 +10638,10 @@
INIT_WORK(&priv->merge_networks,
(void (*)(void *))ipw_merge_adhoc_network, priv);
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
INIT_WORK(&priv->qos_activate, (void (*)(void *))ipw_bg_qos_activate,
priv);
-#endif /* CONFIG_IPW_QOS */
+#endif /* CONFIG_IPW2200_QOS */
tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
ipw_irq_tasklet, (unsigned long)priv);
@@ -10318,12 +10787,21 @@
|= CFG_BT_COEXISTENCE_OOB;
}
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
+ priv->sys_config.accept_all_data_frames = 1;
+ priv->sys_config.accept_non_directed_frames = 1;
+ priv->sys_config.accept_all_mgmt_bcpr = 1;
+ priv->sys_config.accept_all_mgmt_frames = 1;
+ }
+#endif
+
if (priv->ieee->iw_mode == IW_MODE_ADHOC)
priv->sys_config.answer_broadcast_ssid_probe = 1;
else
priv->sys_config.answer_broadcast_ssid_probe = 0;
- if (ipw_send_system_config(priv, &priv->sys_config))
+ if (ipw_send_system_config(priv))
goto error;
init_supported_rates(priv, &priv->rates);
@@ -10335,10 +10813,10 @@
if (ipw_send_rts_threshold(priv, priv->rts_threshold))
goto error;
}
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n");
ipw_qos_activate(priv, NULL);
-#endif /* CONFIG_IPW_QOS */
+#endif /* CONFIG_IPW2200_QOS */
if (ipw_set_random_seed(priv))
goto error;
@@ -10639,6 +11117,7 @@
if (priv->cmdlog == NULL) {
IPW_ERROR("Error allocating %d command log entries.\n",
cmdlog);
+ return -ENOMEM;
} else {
memset(priv->cmdlog, 0, sizeof(*priv->cmdlog) * cmdlog);
priv->cmdlog_len = cmdlog;
@@ -10860,6 +11339,10 @@
&dev_attr_led.attr,
&dev_attr_speed_scan.attr,
&dev_attr_net_stats.attr,
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ &dev_attr_rtap_iface.attr,
+ &dev_attr_rtap_filter.attr,
+#endif
NULL
};
@@ -10868,6 +11351,109 @@
.attrs = ipw_sysfs_entries,
};
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static int ipw_prom_open(struct net_device *dev)
+{
+ struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
+ struct ipw_priv *priv = prom_priv->priv;
+
+ IPW_DEBUG_INFO("prom dev->open\n");
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+
+ if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+ priv->sys_config.accept_all_data_frames = 1;
+ priv->sys_config.accept_non_directed_frames = 1;
+ priv->sys_config.accept_all_mgmt_bcpr = 1;
+ priv->sys_config.accept_all_mgmt_frames = 1;
+
+ ipw_send_system_config(priv);
+ }
+
+ return 0;
+}
+
+static int ipw_prom_stop(struct net_device *dev)
+{
+ struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
+ struct ipw_priv *priv = prom_priv->priv;
+
+ IPW_DEBUG_INFO("prom dev->stop\n");
+
+ if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+ priv->sys_config.accept_all_data_frames = 0;
+ priv->sys_config.accept_non_directed_frames = 0;
+ priv->sys_config.accept_all_mgmt_bcpr = 0;
+ priv->sys_config.accept_all_mgmt_frames = 0;
+
+ ipw_send_system_config(priv);
+ }
+
+ return 0;
+}
+
+static int ipw_prom_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ IPW_DEBUG_INFO("prom dev->xmit\n");
+ netif_stop_queue(dev);
+ return -EOPNOTSUPP;
+}
+
+static struct net_device_stats *ipw_prom_get_stats(struct net_device *dev)
+{
+ struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
+ return &prom_priv->ieee->stats;
+}
+
+static int ipw_prom_alloc(struct ipw_priv *priv)
+{
+ int rc = 0;
+
+ if (priv->prom_net_dev)
+ return -EPERM;
+
+ priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv));
+ if (priv->prom_net_dev == NULL)
+ return -ENOMEM;
+
+ priv->prom_priv = ieee80211_priv(priv->prom_net_dev);
+ priv->prom_priv->ieee = netdev_priv(priv->prom_net_dev);
+ priv->prom_priv->priv = priv;
+
+ strcpy(priv->prom_net_dev->name, "rtap%d");
+
+ priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ priv->prom_net_dev->open = ipw_prom_open;
+ priv->prom_net_dev->stop = ipw_prom_stop;
+ priv->prom_net_dev->get_stats = ipw_prom_get_stats;
+ priv->prom_net_dev->hard_start_xmit = ipw_prom_hard_start_xmit;
+
+ priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR;
+
+ rc = register_netdev(priv->prom_net_dev);
+ if (rc) {
+ free_ieee80211(priv->prom_net_dev);
+ priv->prom_net_dev = NULL;
+ return rc;
+ }
+
+ return 0;
+}
+
+static void ipw_prom_free(struct ipw_priv *priv)
+{
+ if (!priv->prom_net_dev)
+ return;
+
+ unregister_netdev(priv->prom_net_dev);
+ free_ieee80211(priv->prom_net_dev);
+
+ priv->prom_net_dev = NULL;
+}
+
+#endif
+
+
static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int err = 0;
@@ -10959,11 +11545,12 @@
priv->ieee->set_security = shim__set_security;
priv->ieee->is_queue_full = ipw_net_is_queue_full;
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
+ priv->ieee->is_qos_active = ipw_is_qos_active;
priv->ieee->handle_probe_response = ipw_handle_beacon;
priv->ieee->handle_beacon = ipw_handle_probe_response;
priv->ieee->handle_assoc_response = ipw_handle_assoc_response;
-#endif /* CONFIG_IPW_QOS */
+#endif /* CONFIG_IPW2200_QOS */
priv->ieee->perfect_rssi = -20;
priv->ieee->worst_rssi = -85;
@@ -10997,6 +11584,18 @@
goto out_remove_sysfs;
}
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ if (rtap_iface) {
+ err = ipw_prom_alloc(priv);
+ if (err) {
+ IPW_ERROR("Failed to register promiscuous network "
+ "device (error %d).\n", err);
+ unregister_netdev(priv->net_dev);
+ goto out_remove_sysfs;
+ }
+ }
+#endif
+
printk(KERN_INFO DRV_NAME ": Detected geography %s (%d 802.11bg "
"channels, %d 802.11a channels)\n",
priv->ieee->geo.name, priv->ieee->geo.bg_channels,
@@ -11076,6 +11675,10 @@
priv->error = NULL;
}
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ ipw_prom_free(priv);
+#endif
+
free_irq(pdev->irq, priv);
iounmap(priv->hw_base);
pci_release_regions(pdev);
@@ -11200,7 +11803,12 @@
module_param(channel, int, 0444);
MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+module_param(rtap_iface, int, 0444);
+MODULE_PARM_DESC(rtap_iface, "create the rtap interface (1 - create, default 0)");
+#endif
+
+#ifdef CONFIG_IPW2200_QOS
module_param(qos_enable, int, 0444);
MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis");
@@ -11215,7 +11823,7 @@
module_param(burst_duration_OFDM, int, 0444);
MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value");
-#endif /* CONFIG_IPW_QOS */
+#endif /* CONFIG_IPW2200_QOS */
#ifdef CONFIG_IPW2200_MONITOR
module_param(mode, int, 0444);
@@ -11238,5 +11846,8 @@
module_param(roaming, int, 0444);
MODULE_PARM_DESC(roaming, "enable roaming support (default on)");
+module_param(antenna, int, 0444);
+MODULE_PARM_DESC(antenna, "select antenna 1=Main, 3=Aux, default 0 [both], 2=slow_diversity (choose the one with lower background noise)");
+
module_exit(ipw_exit);
module_init(ipw_init);
diff --git a/drivers/net/wireless/ipw2200.h b/drivers/net/wireless/ipw2200.h
index 4b98049..6044c0b 100644
--- a/drivers/net/wireless/ipw2200.h
+++ b/drivers/net/wireless/ipw2200.h
@@ -789,7 +789,7 @@
u8 bt_coexist_collision_thr;
u8 silence_threshold;
u8 accept_all_mgmt_bcpr;
- u8 accept_all_mgtm_frames;
+ u8 accept_all_mgmt_frames;
u8 pass_noise_stats_to_host;
u8 reserved3;
} __attribute__ ((packed));
@@ -1122,6 +1122,52 @@
u8 payload[0];
} __attribute__ ((packed));
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+
+enum ipw_prom_filter {
+ IPW_PROM_CTL_HEADER_ONLY = (1 << 0),
+ IPW_PROM_MGMT_HEADER_ONLY = (1 << 1),
+ IPW_PROM_DATA_HEADER_ONLY = (1 << 2),
+ IPW_PROM_ALL_HEADER_ONLY = 0xf, /* bits 0..3 */
+ IPW_PROM_NO_TX = (1 << 4),
+ IPW_PROM_NO_RX = (1 << 5),
+ IPW_PROM_NO_CTL = (1 << 6),
+ IPW_PROM_NO_MGMT = (1 << 7),
+ IPW_PROM_NO_DATA = (1 << 8),
+};
+
+struct ipw_priv;
+struct ipw_prom_priv {
+ struct ipw_priv *priv;
+ struct ieee80211_device *ieee;
+ enum ipw_prom_filter filter;
+ int tx_packets;
+ int rx_packets;
+};
+#endif
+
+#if defined(CONFIG_IPW2200_RADIOTAP) || defined(CONFIG_IPW2200_PROMISCUOUS)
+/* Magic struct that slots into the radiotap header -- no reason
+ * to build this manually element by element, we can write it much
+ * more efficiently than we can parse it. ORDER MATTERS HERE
+ *
+ * When sent to us via the simulated Rx interface in sysfs, the entire
+ * structure is provided regardless of any bits unset.
+ */
+struct ipw_rt_hdr {
+ struct ieee80211_radiotap_header rt_hdr;
+ u64 rt_tsf; /* TSF */
+ u8 rt_flags; /* radiotap packet flags */
+ u8 rt_rate; /* rate in 500kb/s */
+ u16 rt_channel; /* channel in mhz */
+ u16 rt_chbitmask; /* channel bitfield */
+ s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
+ s8 rt_dbmnoise;
+ u8 rt_antenna; /* antenna number */
+ u8 payload[0]; /* payload... */
+} __attribute__ ((packed));
+#endif
+
struct ipw_priv {
/* ieee device used by generic ieee processing code */
struct ieee80211_device *ieee;
@@ -1133,6 +1179,12 @@
struct pci_dev *pci_dev;
struct net_device *net_dev;
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ /* Promiscuous mode */
+ struct ipw_prom_priv *prom_priv;
+ struct net_device *prom_net_dev;
+#endif
+
/* pci hardware address support */
void __iomem *hw_base;
unsigned long hw_len;
@@ -1153,11 +1205,9 @@
u32 config;
u32 capability;
- u8 last_rx_rssi;
- u8 last_noise;
struct average average_missed_beacons;
- struct average average_rssi;
- struct average average_noise;
+ s16 exp_avg_rssi;
+ s16 exp_avg_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 */
@@ -1308,6 +1358,29 @@
/* debug macros */
+/* Debug and printf string expansion helpers for printing bitfields */
+#define BIT_FMT8 "%c%c%c%c-%c%c%c%c"
+#define BIT_FMT16 BIT_FMT8 ":" BIT_FMT8
+#define BIT_FMT32 BIT_FMT16 " " BIT_FMT16
+
+#define BITC(x,y) (((x>>y)&1)?'1':'0')
+#define BIT_ARG8(x) \
+BITC(x,7),BITC(x,6),BITC(x,5),BITC(x,4),\
+BITC(x,3),BITC(x,2),BITC(x,1),BITC(x,0)
+
+#define BIT_ARG16(x) \
+BITC(x,15),BITC(x,14),BITC(x,13),BITC(x,12),\
+BITC(x,11),BITC(x,10),BITC(x,9),BITC(x,8),\
+BIT_ARG8(x)
+
+#define BIT_ARG32(x) \
+BITC(x,31),BITC(x,30),BITC(x,29),BITC(x,28),\
+BITC(x,27),BITC(x,26),BITC(x,25),BITC(x,24),\
+BITC(x,23),BITC(x,22),BITC(x,21),BITC(x,20),\
+BITC(x,19),BITC(x,18),BITC(x,17),BITC(x,16),\
+BIT_ARG16(x)
+
+
#ifdef CONFIG_IPW2200_DEBUG
#define IPW_DEBUG(level, fmt, args...) \
do { if (ipw_debug_level & (level)) \
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 06523e2..d4c13ff 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -201,41 +201,12 @@
/* Data types */
/********************************************************************/
-/* Used in Event handling.
- * We avoid nested structures as they break on ARM -- Moustafa */
-struct hermes_tx_descriptor_802_11 {
- /* hermes_tx_descriptor */
- __le16 status;
- __le16 reserved1;
- __le16 reserved2;
- __le32 sw_support;
- u8 retry_count;
- u8 tx_rate;
- __le16 tx_control;
-
- /* ieee80211_hdr */
+/* Beginning of the Tx descriptor, used in TxExc handling */
+struct hermes_txexc_data {
+ struct hermes_tx_descriptor desc;
__le16 frame_ctl;
__le16 duration_id;
u8 addr1[ETH_ALEN];
- u8 addr2[ETH_ALEN];
- u8 addr3[ETH_ALEN];
- __le16 seq_ctl;
- u8 addr4[ETH_ALEN];
-
- __le16 data_len;
-
- /* ethhdr */
- u8 h_dest[ETH_ALEN]; /* destination eth addr */
- u8 h_source[ETH_ALEN]; /* source ether addr */
- __be16 h_proto; /* packet type ID field */
-
- /* p8022_hdr */
- u8 dsap;
- u8 ssap;
- u8 ctrl;
- u8 oui[3];
-
- __be16 ethertype;
} __attribute__ ((packed));
/* Rx frame header except compatibility 802.3 header */
@@ -450,53 +421,39 @@
hermes_t *hw = &priv->hw;
int err = 0;
u16 txfid = priv->txfid;
- char *p;
struct ethhdr *eh;
- int len, data_len, data_off;
+ int data_off;
struct hermes_tx_descriptor desc;
unsigned long flags;
- TRACE_ENTER(dev->name);
-
if (! netif_running(dev)) {
printk(KERN_ERR "%s: Tx on stopped device!\n",
dev->name);
- TRACE_EXIT(dev->name);
- return 1;
+ return NETDEV_TX_BUSY;
}
if (netif_queue_stopped(dev)) {
printk(KERN_DEBUG "%s: Tx while transmitter busy!\n",
dev->name);
- TRACE_EXIT(dev->name);
- return 1;
+ return NETDEV_TX_BUSY;
}
if (orinoco_lock(priv, &flags) != 0) {
printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n",
dev->name);
- TRACE_EXIT(dev->name);
- return 1;
+ return NETDEV_TX_BUSY;
}
if (! netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) {
/* Oops, the firmware hasn't established a connection,
silently drop the packet (this seems to be the
safest approach). */
- stats->tx_errors++;
- orinoco_unlock(priv, &flags);
- dev_kfree_skb(skb);
- TRACE_EXIT(dev->name);
- return 0;
+ goto drop;
}
- /* Length of the packet body */
- /* FIXME: what if the skb is smaller than this? */
- len = max_t(int, ALIGN(skb->len, 2), ETH_ZLEN);
- skb = skb_padto(skb, len);
- if (skb == NULL)
- goto fail;
- len -= ETH_HLEN;
+ /* Check packet length */
+ if (skb->len < ETH_HLEN)
+ goto drop;
eh = (struct ethhdr *)skb->data;
@@ -507,8 +464,7 @@
if (net_ratelimit())
printk(KERN_ERR "%s: Error %d writing Tx descriptor "
"to BAP\n", dev->name, err);
- stats->tx_errors++;
- goto fail;
+ goto busy;
}
/* Clear the 802.11 header and data length fields - some
@@ -519,50 +475,38 @@
/* Encapsulate Ethernet-II frames */
if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
- struct header_struct hdr;
- data_len = len;
- data_off = HERMES_802_3_OFFSET + sizeof(hdr);
- p = skb->data + ETH_HLEN;
+ struct header_struct {
+ struct ethhdr eth; /* 802.3 header */
+ u8 encap[6]; /* 802.2 header */
+ } __attribute__ ((packed)) hdr;
- /* 802.3 header */
- memcpy(hdr.dest, eh->h_dest, ETH_ALEN);
- memcpy(hdr.src, eh->h_source, ETH_ALEN);
- hdr.len = htons(data_len + ENCAPS_OVERHEAD);
-
- /* 802.2 header */
- memcpy(&hdr.dsap, &encaps_hdr, sizeof(encaps_hdr));
-
- hdr.ethertype = eh->h_proto;
- err = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
- txfid, HERMES_802_3_OFFSET);
+ /* Strip destination and source from the data */
+ skb_pull(skb, 2 * ETH_ALEN);
+ data_off = HERMES_802_2_OFFSET + sizeof(encaps_hdr);
+
+ /* And move them to a separate header */
+ memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
+ hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len);
+ memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));
+
+ err = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
+ txfid, HERMES_802_3_OFFSET);
if (err) {
if (net_ratelimit())
printk(KERN_ERR "%s: Error %d writing packet "
"header to BAP\n", dev->name, err);
- stats->tx_errors++;
- goto fail;
+ goto busy;
}
- /* Actual xfer length - allow for padding */
- len = ALIGN(data_len, 2);
- if (len < ETH_ZLEN - ETH_HLEN)
- len = ETH_ZLEN - ETH_HLEN;
} else { /* IEEE 802.3 frame */
- data_len = len + ETH_HLEN;
data_off = HERMES_802_3_OFFSET;
- p = skb->data;
- /* Actual xfer length - round up for odd length packets */
- len = ALIGN(data_len, 2);
- if (len < ETH_ZLEN)
- len = ETH_ZLEN;
}
- err = hermes_bap_pwrite_pad(hw, USER_BAP, p, data_len, len,
+ err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len,
txfid, data_off);
if (err) {
printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
dev->name, err);
- stats->tx_errors++;
- goto fail;
+ goto busy;
}
/* Finally, we actually initiate the send */
@@ -575,25 +519,27 @@
if (net_ratelimit())
printk(KERN_ERR "%s: Error %d transmitting packet\n",
dev->name, err);
- stats->tx_errors++;
- goto fail;
+ goto busy;
}
dev->trans_start = jiffies;
- stats->tx_bytes += data_off + data_len;
+ stats->tx_bytes += data_off + skb->len;
+ goto ok;
+ drop:
+ stats->tx_errors++;
+ stats->tx_dropped++;
+
+ ok:
orinoco_unlock(priv, &flags);
-
dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
- TRACE_EXIT(dev->name);
-
- return 0;
- fail:
- TRACE_EXIT(dev->name);
-
+ busy:
+ if (err == -EIO)
+ schedule_work(&priv->reset_work);
orinoco_unlock(priv, &flags);
- return err;
+ return NETDEV_TX_BUSY;
}
static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
@@ -629,7 +575,7 @@
struct net_device_stats *stats = &priv->stats;
u16 fid = hermes_read_regn(hw, TXCOMPLFID);
u16 status;
- struct hermes_tx_descriptor_802_11 hdr;
+ struct hermes_txexc_data hdr;
int err = 0;
if (fid == DUMMY_FID)
@@ -637,8 +583,7 @@
/* Read part of the frame header - we need status and addr1 */
err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
- offsetof(struct hermes_tx_descriptor_802_11,
- addr2),
+ sizeof(struct hermes_txexc_data),
fid, 0);
hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
@@ -658,7 +603,7 @@
* 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 */
- status = le16_to_cpu(hdr.status);
+ status = le16_to_cpu(hdr.desc.status);
if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) {
union iwreq_data wrqu;
@@ -1400,16 +1345,12 @@
return 0;
}
-int orinoco_reinit_firmware(struct net_device *dev)
+static int orinoco_allocate_fid(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);
struct hermes *hw = &priv->hw;
int err;
- err = hermes_init(hw);
- if (err)
- return err;
-
err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
/* Try workaround for old Symbol firmware bug */
@@ -1428,6 +1369,19 @@
return err;
}
+int orinoco_reinit_firmware(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct hermes *hw = &priv->hw;
+ int err;
+
+ err = hermes_init(hw);
+ if (!err)
+ err = orinoco_allocate_fid(dev);
+
+ return err;
+}
+
static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
{
hermes_t *hw = &priv->hw;
@@ -2274,14 +2228,12 @@
u16 reclen;
int len;
- TRACE_ENTER(dev->name);
-
/* No need to lock, the hw_unavailable flag is already set in
* alloc_orinocodev() */
priv->nicbuf_size = IEEE80211_FRAME_LEN + ETH_HLEN;
/* Initialize the firmware */
- err = orinoco_reinit_firmware(dev);
+ err = hermes_init(hw);
if (err != 0) {
printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n",
dev->name, err);
@@ -2339,6 +2291,13 @@
printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick);
+ err = orinoco_allocate_fid(dev);
+ if (err) {
+ printk(KERN_ERR "%s: failed to allocate NIC buffer!\n",
+ dev->name);
+ goto out;
+ }
+
/* Get allowed channels */
err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
&priv->channel_mask);
@@ -2429,7 +2388,6 @@
printk(KERN_DEBUG "%s: ready\n", dev->name);
out:
- TRACE_EXIT(dev->name);
return err;
}
@@ -2797,8 +2755,6 @@
int numrates;
int i, k;
- TRACE_ENTER(dev->name);
-
rrq->length = sizeof(struct iw_range);
memset(range, 0, sizeof(struct iw_range));
@@ -2888,8 +2844,6 @@
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
- TRACE_EXIT(dev->name);
-
return 0;
}
@@ -3071,8 +3025,6 @@
int err = 0;
unsigned long flags;
- TRACE_ENTER(dev->name);
-
if (netif_running(dev)) {
err = orinoco_hw_get_essid(priv, &active, essidbuf);
if (err)
@@ -3087,8 +3039,6 @@
erq->flags = 1;
erq->length = strlen(essidbuf) + 1;
- TRACE_EXIT(dev->name);
-
return 0;
}
@@ -4349,69 +4299,6 @@
};
/********************************************************************/
-/* Debugging */
-/********************************************************************/
-
-#if 0
-static void show_rx_frame(struct orinoco_rxframe_hdr *frame)
-{
- printk(KERN_DEBUG "RX descriptor:\n");
- printk(KERN_DEBUG " status = 0x%04x\n", frame->desc.status);
- printk(KERN_DEBUG " time = 0x%08x\n", frame->desc.time);
- printk(KERN_DEBUG " silence = 0x%02x\n", frame->desc.silence);
- printk(KERN_DEBUG " signal = 0x%02x\n", frame->desc.signal);
- printk(KERN_DEBUG " rate = 0x%02x\n", frame->desc.rate);
- printk(KERN_DEBUG " rxflow = 0x%02x\n", frame->desc.rxflow);
- printk(KERN_DEBUG " reserved = 0x%08x\n", frame->desc.reserved);
-
- printk(KERN_DEBUG "IEEE 802.11 header:\n");
- printk(KERN_DEBUG " frame_ctl = 0x%04x\n",
- frame->p80211.frame_ctl);
- printk(KERN_DEBUG " duration_id = 0x%04x\n",
- frame->p80211.duration_id);
- printk(KERN_DEBUG " addr1 = %02x:%02x:%02x:%02x:%02x:%02x\n",
- frame->p80211.addr1[0], frame->p80211.addr1[1],
- frame->p80211.addr1[2], frame->p80211.addr1[3],
- frame->p80211.addr1[4], frame->p80211.addr1[5]);
- printk(KERN_DEBUG " addr2 = %02x:%02x:%02x:%02x:%02x:%02x\n",
- frame->p80211.addr2[0], frame->p80211.addr2[1],
- frame->p80211.addr2[2], frame->p80211.addr2[3],
- frame->p80211.addr2[4], frame->p80211.addr2[5]);
- printk(KERN_DEBUG " addr3 = %02x:%02x:%02x:%02x:%02x:%02x\n",
- frame->p80211.addr3[0], frame->p80211.addr3[1],
- frame->p80211.addr3[2], frame->p80211.addr3[3],
- frame->p80211.addr3[4], frame->p80211.addr3[5]);
- printk(KERN_DEBUG " seq_ctl = 0x%04x\n",
- frame->p80211.seq_ctl);
- printk(KERN_DEBUG " addr4 = %02x:%02x:%02x:%02x:%02x:%02x\n",
- frame->p80211.addr4[0], frame->p80211.addr4[1],
- frame->p80211.addr4[2], frame->p80211.addr4[3],
- frame->p80211.addr4[4], frame->p80211.addr4[5]);
- printk(KERN_DEBUG " data_len = 0x%04x\n",
- frame->p80211.data_len);
-
- printk(KERN_DEBUG "IEEE 802.3 header:\n");
- printk(KERN_DEBUG " dest = %02x:%02x:%02x:%02x:%02x:%02x\n",
- frame->p8023.h_dest[0], frame->p8023.h_dest[1],
- frame->p8023.h_dest[2], frame->p8023.h_dest[3],
- frame->p8023.h_dest[4], frame->p8023.h_dest[5]);
- printk(KERN_DEBUG " src = %02x:%02x:%02x:%02x:%02x:%02x\n",
- frame->p8023.h_source[0], frame->p8023.h_source[1],
- frame->p8023.h_source[2], frame->p8023.h_source[3],
- frame->p8023.h_source[4], frame->p8023.h_source[5]);
- printk(KERN_DEBUG " len = 0x%04x\n", frame->p8023.h_proto);
-
- printk(KERN_DEBUG "IEEE 802.2 LLC/SNAP header:\n");
- printk(KERN_DEBUG " DSAP = 0x%02x\n", frame->p8022.dsap);
- printk(KERN_DEBUG " SSAP = 0x%02x\n", frame->p8022.ssap);
- printk(KERN_DEBUG " ctrl = 0x%02x\n", frame->p8022.ctrl);
- printk(KERN_DEBUG " OUI = %02x:%02x:%02x\n",
- frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]);
- printk(KERN_DEBUG " ethertype = 0x%04x\n", frame->ethertype);
-}
-#endif /* 0 */
-
-/********************************************************************/
/* Module initialization */
/********************************************************************/
diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index f5d856d..16db3e1 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -7,7 +7,7 @@
#ifndef _ORINOCO_H
#define _ORINOCO_H
-#define DRIVER_VERSION "0.15rc3"
+#define DRIVER_VERSION "0.15"
#include <linux/netdevice.h>
#include <linux/wireless.h>
@@ -30,20 +30,6 @@
char data[ORINOCO_MAX_KEY_SIZE];
} __attribute__ ((packed));
-struct header_struct {
- /* 802.3 */
- u8 dest[ETH_ALEN];
- u8 src[ETH_ALEN];
- __be16 len;
- /* 802.2 */
- u8 dsap;
- u8 ssap;
- u8 ctrl;
- /* SNAP */
- u8 oui[3];
- unsigned short ethertype;
-} __attribute__ ((packed));
-
typedef enum {
FIRMWARE_TYPE_AGERE,
FIRMWARE_TYPE_INTERSIL,
@@ -132,9 +118,6 @@
#define DEBUG(n, args...) do { } while (0)
#endif /* ORINOCO_DEBUG */
-#define TRACE_ENTER(devname) DEBUG(2, "%s: -> %s()\n", devname, __FUNCTION__);
-#define TRACE_EXIT(devname) DEBUG(2, "%s: <- %s()\n", devname, __FUNCTION__);
-
/********************************************************************/
/* Exported prototypes */
/********************************************************************/
diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c
index 434f7d7..d2c48ac 100644
--- a/drivers/net/wireless/orinoco_cs.c
+++ b/drivers/net/wireless/orinoco_cs.c
@@ -178,13 +178,10 @@
int last_fn, last_ret;
u_char buf[64];
config_info_t conf;
- cisinfo_t info;
tuple_t tuple;
cisparse_t parse;
void __iomem *mem;
- CS_CHECK(ValidateCIS, pcmcia_validate_cis(link, &info));
-
/*
* This reads the card's CONFIG tuple to find its
* configuration registers.
@@ -234,12 +231,6 @@
goto next_entry;
link->conf.ConfigIndex = cfg->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)) {
diff --git a/drivers/net/wireless/orinoco_nortel.c b/drivers/net/wireless/orinoco_nortel.c
index d1a670b..1596182 100644
--- a/drivers/net/wireless/orinoco_nortel.c
+++ b/drivers/net/wireless/orinoco_nortel.c
@@ -1,9 +1,8 @@
/* orinoco_nortel.c
- *
+ *
* Driver for Prism II devices which would usually be driven by orinoco_cs,
* but are connected to the PCI bus by a PCI-to-PCMCIA adapter used in
* Nortel emobility, Symbol LA-4113 and Symbol LA-4123.
- * but are connected to the PCI bus by a Nortel PCI-PCMCIA-Adapter.
*
* Copyright (C) 2002 Tobias Hoffmann
* (C) 2003 Christoph Jungegger <disdos@traum404.de>
@@ -50,67 +49,62 @@
#include <pcmcia/cisreg.h>
#include "orinoco.h"
+#include "orinoco_pci.h"
#define COR_OFFSET (0xe0) /* COR attribute offset of Prism2 PC card */
#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
-/* Nortel specific data */
-struct nortel_pci_card {
- unsigned long iobase1;
- unsigned long iobase2;
-};
-
/*
- * Do a soft reset of the PCI card using the Configuration Option Register
+ * Do a soft reset of the card using the Configuration Option Register
* We need this to get going...
* This is the part of the code that is strongly inspired from wlan-ng
*
* Note bis : Don't try to access HERMES_CMD during the reset phase.
* It just won't work !
*/
-static int nortel_pci_cor_reset(struct orinoco_private *priv)
+static int orinoco_nortel_cor_reset(struct orinoco_private *priv)
{
- struct nortel_pci_card *card = priv->card;
+ struct orinoco_pci_card *card = priv->card;
- /* Assert the reset until the card notice */
- outw_p(8, card->iobase1 + 2);
- inw(card->iobase2 + COR_OFFSET);
- outw_p(0x80, card->iobase2 + COR_OFFSET);
+ /* Assert the reset until the card notices */
+ iowrite16(8, card->bridge_io + 2);
+ ioread16(card->attr_io + COR_OFFSET);
+ iowrite16(0x80, card->attr_io + COR_OFFSET);
mdelay(1);
/* Give time for the card to recover from this hard effort */
- outw_p(0, card->iobase2 + COR_OFFSET);
- outw_p(0, card->iobase2 + COR_OFFSET);
+ iowrite16(0, card->attr_io + COR_OFFSET);
+ iowrite16(0, card->attr_io + COR_OFFSET);
mdelay(1);
- /* set COR as usual */
- outw_p(COR_VALUE, card->iobase2 + COR_OFFSET);
- outw_p(COR_VALUE, card->iobase2 + COR_OFFSET);
+ /* Set COR as usual */
+ iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
+ iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
mdelay(1);
- outw_p(0x228, card->iobase1 + 2);
+ iowrite16(0x228, card->bridge_io + 2);
return 0;
}
-static int nortel_pci_hw_init(struct nortel_pci_card *card)
+static int orinoco_nortel_hw_init(struct orinoco_pci_card *card)
{
int i;
u32 reg;
- /* setup bridge */
- if (inw(card->iobase1) & 1) {
+ /* Setup bridge */
+ if (ioread16(card->bridge_io) & 1) {
printk(KERN_ERR PFX "brg1 answer1 wrong\n");
return -EBUSY;
}
- outw_p(0x118, card->iobase1 + 2);
- outw_p(0x108, card->iobase1 + 2);
+ iowrite16(0x118, card->bridge_io + 2);
+ iowrite16(0x108, card->bridge_io + 2);
mdelay(30);
- outw_p(0x8, card->iobase1 + 2);
+ iowrite16(0x8, card->bridge_io + 2);
for (i = 0; i < 30; i++) {
mdelay(30);
- if (inw(card->iobase1) & 0x10) {
+ if (ioread16(card->bridge_io) & 0x10) {
break;
}
}
@@ -118,42 +112,42 @@
printk(KERN_ERR PFX "brg1 timed out\n");
return -EBUSY;
}
- if (inw(card->iobase2 + 0xe0) & 1) {
+ if (ioread16(card->attr_io + COR_OFFSET) & 1) {
printk(KERN_ERR PFX "brg2 answer1 wrong\n");
return -EBUSY;
}
- if (inw(card->iobase2 + 0xe2) & 1) {
+ if (ioread16(card->attr_io + COR_OFFSET + 2) & 1) {
printk(KERN_ERR PFX "brg2 answer2 wrong\n");
return -EBUSY;
}
- if (inw(card->iobase2 + 0xe4) & 1) {
+ if (ioread16(card->attr_io + COR_OFFSET + 4) & 1) {
printk(KERN_ERR PFX "brg2 answer3 wrong\n");
return -EBUSY;
}
- /* set the PCMCIA COR-Register */
- outw_p(COR_VALUE, card->iobase2 + COR_OFFSET);
+ /* Set the PCMCIA COR register */
+ iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
mdelay(1);
- reg = inw(card->iobase2 + COR_OFFSET);
+ reg = ioread16(card->attr_io + COR_OFFSET);
if (reg != COR_VALUE) {
printk(KERN_ERR PFX "Error setting COR value (reg=%x)\n",
reg);
return -EBUSY;
}
- /* set leds */
- outw_p(1, card->iobase1 + 10);
+ /* Set LEDs */
+ iowrite16(1, card->bridge_io + 10);
return 0;
}
-static int nortel_pci_init_one(struct pci_dev *pdev,
- const struct pci_device_id *ent)
+static int orinoco_nortel_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
{
int err;
struct orinoco_private *priv;
- struct nortel_pci_card *card;
+ struct orinoco_pci_card *card;
struct net_device *dev;
- void __iomem *iomem;
+ void __iomem *hermes_io, *bridge_io, *attr_io;
err = pci_enable_device(pdev);
if (err) {
@@ -162,19 +156,34 @@
}
err = pci_request_regions(pdev, DRIVER_NAME);
- if (err != 0) {
+ if (err) {
printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
goto fail_resources;
}
- iomem = pci_iomap(pdev, 2, 0);
- if (!iomem) {
- err = -ENOMEM;
- goto fail_map_io;
+ bridge_io = pci_iomap(pdev, 0, 0);
+ if (!bridge_io) {
+ printk(KERN_ERR PFX "Cannot map bridge registers\n");
+ err = -EIO;
+ goto fail_map_bridge;
+ }
+
+ attr_io = pci_iomap(pdev, 1, 0);
+ if (!attr_io) {
+ printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n");
+ err = -EIO;
+ goto fail_map_attr;
+ }
+
+ hermes_io = pci_iomap(pdev, 2, 0);
+ if (!hermes_io) {
+ printk(KERN_ERR PFX "Cannot map chipset registers\n");
+ err = -EIO;
+ goto fail_map_hermes;
}
/* Allocate network device */
- dev = alloc_orinocodev(sizeof(*card), nortel_pci_cor_reset);
+ dev = alloc_orinocodev(sizeof(*card), orinoco_nortel_cor_reset);
if (!dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
err = -ENOMEM;
@@ -183,16 +192,12 @@
priv = netdev_priv(dev);
card = priv->card;
- card->iobase1 = pci_resource_start(pdev, 0);
- card->iobase2 = pci_resource_start(pdev, 1);
- dev->base_addr = pci_resource_start(pdev, 2);
+ card->bridge_io = bridge_io;
+ card->attr_io = attr_io;
SET_MODULE_OWNER(dev);
SET_NETDEV_DEV(dev, &pdev->dev);
- hermes_struct_init(&priv->hw, iomem, HERMES_16BIT_REGSPACING);
-
- printk(KERN_DEBUG PFX "Detected Nortel PCI device at %s irq:%d, "
- "io addr:0x%lx\n", pci_name(pdev), pdev->irq, dev->base_addr);
+ hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING);
err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
dev->name, dev);
@@ -201,21 +206,20 @@
err = -EBUSY;
goto fail_irq;
}
- dev->irq = pdev->irq;
+ orinoco_pci_setup_netdev(dev, pdev, 2);
- err = nortel_pci_hw_init(card);
+ err = orinoco_nortel_hw_init(card);
if (err) {
printk(KERN_ERR PFX "Hardware initialization failed\n");
goto fail;
}
- err = nortel_pci_cor_reset(priv);
+ err = orinoco_nortel_cor_reset(priv);
if (err) {
printk(KERN_ERR PFX "Initial reset failed\n");
goto fail;
}
-
err = register_netdev(dev);
if (err) {
printk(KERN_ERR PFX "Cannot register network device\n");
@@ -234,9 +238,15 @@
free_orinocodev(dev);
fail_alloc:
- pci_iounmap(pdev, iomem);
+ pci_iounmap(pdev, hermes_io);
- fail_map_io:
+ fail_map_hermes:
+ pci_iounmap(pdev, attr_io);
+
+ fail_map_attr:
+ pci_iounmap(pdev, bridge_io);
+
+ fail_map_bridge:
pci_release_regions(pdev);
fail_resources:
@@ -245,26 +255,27 @@
return err;
}
-static void __devexit nortel_pci_remove_one(struct pci_dev *pdev)
+static void __devexit orinoco_nortel_remove_one(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct orinoco_private *priv = netdev_priv(dev);
- struct nortel_pci_card *card = priv->card;
+ struct orinoco_pci_card *card = priv->card;
- /* clear leds */
- outw_p(0, card->iobase1 + 10);
+ /* Clear LEDs */
+ iowrite16(0, card->bridge_io + 10);
unregister_netdev(dev);
free_irq(dev->irq, dev);
pci_set_drvdata(pdev, NULL);
free_orinocodev(dev);
pci_iounmap(pdev, priv->hw.iobase);
+ pci_iounmap(pdev, card->attr_io);
+ pci_iounmap(pdev, card->bridge_io);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
-
-static struct pci_device_id nortel_pci_id_table[] = {
+static struct pci_device_id orinoco_nortel_id_table[] = {
/* Nortel emobility PCI */
{0x126c, 0x8030, PCI_ANY_ID, PCI_ANY_ID,},
/* Symbol LA-4123 PCI */
@@ -272,13 +283,15 @@
{0,},
};
-MODULE_DEVICE_TABLE(pci, nortel_pci_id_table);
+MODULE_DEVICE_TABLE(pci, orinoco_nortel_id_table);
-static struct pci_driver nortel_pci_driver = {
- .name = DRIVER_NAME,
- .id_table = nortel_pci_id_table,
- .probe = nortel_pci_init_one,
- .remove = __devexit_p(nortel_pci_remove_one),
+static struct pci_driver orinoco_nortel_driver = {
+ .name = DRIVER_NAME,
+ .id_table = orinoco_nortel_id_table,
+ .probe = orinoco_nortel_init_one,
+ .remove = __devexit_p(orinoco_nortel_remove_one),
+ .suspend = orinoco_pci_suspend,
+ .resume = orinoco_pci_resume,
};
static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
@@ -288,20 +301,19 @@
("Driver for wireless LAN cards using the Nortel PCI bridge");
MODULE_LICENSE("Dual MPL/GPL");
-static int __init nortel_pci_init(void)
+static int __init orinoco_nortel_init(void)
{
printk(KERN_DEBUG "%s\n", version);
- return pci_module_init(&nortel_pci_driver);
+ return pci_module_init(&orinoco_nortel_driver);
}
-static void __exit nortel_pci_exit(void)
+static void __exit orinoco_nortel_exit(void)
{
- pci_unregister_driver(&nortel_pci_driver);
- ssleep(1);
+ pci_unregister_driver(&orinoco_nortel_driver);
}
-module_init(nortel_pci_init);
-module_exit(nortel_pci_exit);
+module_init(orinoco_nortel_init);
+module_exit(orinoco_nortel_exit);
/*
* Local variables:
diff --git a/drivers/net/wireless/orinoco_pci.c b/drivers/net/wireless/orinoco_pci.c
index 5362c21..df37b95 100644
--- a/drivers/net/wireless/orinoco_pci.c
+++ b/drivers/net/wireless/orinoco_pci.c
@@ -1,11 +1,11 @@
/* orinoco_pci.c
*
- * Driver for Prism II devices that have a direct PCI interface
- * (i.e., not in a Pcmcia or PLX bridge)
+ * Driver for Prism 2.5/3 devices that have a direct PCI interface
+ * (i.e. these are not PCMCIA cards in a PCMCIA-to-PCI bridge).
+ * The card contains only one PCI region, which contains all the usual
+ * hermes registers, as well as the COR register.
*
- * Specifically here we're talking about the Linksys WMP11
- *
- * Current maintainers (as of 29 September 2003) are:
+ * Current maintainers are:
* Pavel Roskin <proski AT gnu.org>
* and David Gibson <hermes AT gibson.dropbear.id.au>
*
@@ -41,54 +41,6 @@
* under either the MPL or the GPL.
*/
-/*
- * Theory of operation...
- * -------------------
- * Maybe you had a look in orinoco_plx. Well, this is totally different...
- *
- * The card contains only one PCI region, which contains all the usual
- * hermes registers.
- *
- * The driver will memory map this region in normal memory. Because
- * the hermes registers are mapped in normal memory and not in ISA I/O
- * post space, we can't use the usual inw/outw macros and we need to
- * use readw/writew.
- * This slight difference force us to compile our own version of
- * hermes.c with the register access macro changed. That's a bit
- * hackish but works fine.
- *
- * Note that the PCI region is pretty big (4K). That's much more than
- * the usual set of hermes register (0x0 -> 0x3E). I've got a strong
- * suspicion that the whole memory space of the adapter is in fact in
- * this region. Accessing directly the adapter memory instead of going
- * through the usual register would speed up significantely the
- * operations...
- *
- * Finally, the card looks like this :
------------------------
- Bus 0, device 14, function 0:
- Network controller: PCI device 1260:3873 (Harris Semiconductor) (rev 1).
- IRQ 11.
- Master Capable. Latency=248.
- Prefetchable 32 bit memory at 0xffbcc000 [0xffbccfff].
------------------------
-00:0e.0 Network controller: Harris Semiconductor: Unknown device 3873 (rev 01)
- Subsystem: Unknown device 1737:3874
- Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B-
- Status: Cap+ 66Mhz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR-
- Latency: 248 set, cache line size 08
- Interrupt: pin A routed to IRQ 11
- Region 0: Memory at ffbcc000 (32-bit, prefetchable) [size=4K]
- Capabilities: [dc] Power Management version 2
- Flags: PMEClk- AuxPwr- DSI- D1+ D2+ PME+
- Status: D0 PME-Enable- DSel=0 DScale=0 PME-
------------------------
- *
- * That's all..
- *
- * Jean II
- */
-
#define DRIVER_NAME "orinoco_pci"
#define PFX DRIVER_NAME ": "
@@ -100,12 +52,14 @@
#include <linux/pci.h>
#include "orinoco.h"
+#include "orinoco_pci.h"
-/* All the magic there is from wlan-ng */
-/* Magic offset of the reset register of the PCI card */
+/* Offset of the COR register of the PCI card */
#define HERMES_PCI_COR (0x26)
-/* Magic bitmask to reset the card */
+
+/* Bitmask to reset the card */
#define HERMES_PCI_COR_MASK (0x0080)
+
/* Magic timeouts for doing the reset.
* Those times are straight from wlan-ng, and it is claimed that they
* are necessary. Alan will kill me. Take your time and grab a coffee. */
@@ -113,13 +67,8 @@
#define HERMES_PCI_COR_OFFT (500) /* ms */
#define HERMES_PCI_COR_BUSYT (500) /* ms */
-/* Orinoco PCI specific data */
-struct orinoco_pci_card {
- void __iomem *pci_ioaddr;
-};
-
/*
- * Do a soft reset of the PCI card using the Configuration Option Register
+ * Do a soft reset of the card using the Configuration Option Register
* We need this to get going...
* This is the part of the code that is strongly inspired from wlan-ng
*
@@ -131,14 +80,13 @@
* Note bis : Don't try to access HERMES_CMD during the reset phase.
* It just won't work !
*/
-static int
-orinoco_pci_cor_reset(struct orinoco_private *priv)
+static int orinoco_pci_cor_reset(struct orinoco_private *priv)
{
hermes_t *hw = &priv->hw;
- unsigned long timeout;
- u16 reg;
+ unsigned long timeout;
+ u16 reg;
- /* Assert the reset until the card notice */
+ /* Assert the reset until the card notices */
hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK);
mdelay(HERMES_PCI_COR_ONT);
@@ -163,19 +111,14 @@
return 0;
}
-/*
- * Initialise a card. Mostly similar to PLX code.
- */
static int orinoco_pci_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- int err = 0;
- unsigned long pci_iorange;
- u16 __iomem *pci_ioaddr = NULL;
- unsigned long pci_iolen;
- struct orinoco_private *priv = NULL;
+ int err;
+ struct orinoco_private *priv;
struct orinoco_pci_card *card;
- struct net_device *dev = NULL;
+ struct net_device *dev;
+ void __iomem *hermes_io;
err = pci_enable_device(pdev);
if (err) {
@@ -184,39 +127,32 @@
}
err = pci_request_regions(pdev, DRIVER_NAME);
- if (err != 0) {
+ if (err) {
printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
goto fail_resources;
}
- /* Resource 0 is mapped to the hermes registers */
- pci_iorange = pci_resource_start(pdev, 0);
- pci_iolen = pci_resource_len(pdev, 0);
- pci_ioaddr = ioremap(pci_iorange, pci_iolen);
- if (!pci_iorange) {
- printk(KERN_ERR PFX "Cannot remap hardware registers\n");
- goto fail_map;
+ hermes_io = pci_iomap(pdev, 0, 0);
+ if (!hermes_io) {
+ printk(KERN_ERR PFX "Cannot remap chipset registers\n");
+ err = -EIO;
+ goto fail_map_hermes;
}
/* Allocate network device */
dev = alloc_orinocodev(sizeof(*card), orinoco_pci_cor_reset);
- if (! dev) {
+ if (!dev) {
+ printk(KERN_ERR PFX "Cannot allocate network device\n");
err = -ENOMEM;
goto fail_alloc;
}
priv = netdev_priv(dev);
card = priv->card;
- card->pci_ioaddr = pci_ioaddr;
- dev->mem_start = pci_iorange;
- dev->mem_end = pci_iorange + pci_iolen - 1;
SET_MODULE_OWNER(dev);
SET_NETDEV_DEV(dev, &pdev->dev);
- hermes_struct_init(&priv->hw, pci_ioaddr, HERMES_32BIT_REGSPACING);
-
- printk(KERN_DEBUG PFX "Detected device %s, mem:0x%lx-0x%lx, irq %d\n",
- pci_name(pdev), dev->mem_start, dev->mem_end, pdev->irq);
+ hermes_struct_init(&priv->hw, hermes_io, HERMES_32BIT_REGSPACING);
err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
dev->name, dev);
@@ -225,9 +161,8 @@
err = -EBUSY;
goto fail_irq;
}
- dev->irq = pdev->irq;
+ orinoco_pci_setup_netdev(dev, pdev, 0);
- /* Perform a COR reset to start the card */
err = orinoco_pci_cor_reset(priv);
if (err) {
printk(KERN_ERR PFX "Initial reset failed\n");
@@ -236,7 +171,7 @@
err = register_netdev(dev);
if (err) {
- printk(KERN_ERR PFX "Failed to register net device\n");
+ printk(KERN_ERR PFX "Cannot register network device\n");
goto fail;
}
@@ -252,9 +187,9 @@
free_orinocodev(dev);
fail_alloc:
- iounmap(pci_ioaddr);
+ pci_iounmap(pdev, hermes_io);
- fail_map:
+ fail_map_hermes:
pci_release_regions(pdev);
fail_resources:
@@ -267,87 +202,17 @@
{
struct net_device *dev = pci_get_drvdata(pdev);
struct orinoco_private *priv = netdev_priv(dev);
- struct orinoco_pci_card *card = priv->card;
unregister_netdev(dev);
free_irq(dev->irq, dev);
pci_set_drvdata(pdev, NULL);
free_orinocodev(dev);
- iounmap(card->pci_ioaddr);
+ pci_iounmap(pdev, priv->hw.iobase);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
-static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
- int err;
-
-
- err = orinoco_lock(priv, &flags);
- if (err) {
- printk(KERN_ERR "%s: hw_unavailable on orinoco_pci_suspend\n",
- dev->name);
- return err;
- }
-
- err = __orinoco_down(dev);
- if (err)
- printk(KERN_WARNING "%s: orinoco_pci_suspend(): Error %d downing interface\n",
- dev->name, err);
-
- netif_device_detach(dev);
-
- priv->hw_unavailable++;
-
- orinoco_unlock(priv, &flags);
-
- pci_save_state(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
-
- return 0;
-}
-
-static int orinoco_pci_resume(struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
- int err;
-
- printk(KERN_DEBUG "%s: Orinoco-PCI waking up\n", dev->name);
-
- pci_set_power_state(pdev, 0);
- pci_restore_state(pdev);
-
- err = orinoco_reinit_firmware(dev);
- if (err) {
- printk(KERN_ERR "%s: Error %d re-initializing firmware on orinoco_pci_resume()\n",
- dev->name, err);
- return err;
- }
-
- spin_lock_irqsave(&priv->lock, flags);
-
- netif_device_attach(dev);
-
- priv->hw_unavailable--;
-
- if (priv->open && (! priv->hw_unavailable)) {
- err = __orinoco_up(dev);
- if (err)
- printk(KERN_ERR "%s: Error %d restarting card on orinoco_pci_resume()\n",
- dev->name, err);
- }
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return 0;
-}
-
-static struct pci_device_id orinoco_pci_pci_id_table[] = {
+static struct pci_device_id orinoco_pci_id_table[] = {
/* Intersil Prism 3 */
{0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,},
/* Intersil Prism 2.5 */
@@ -357,11 +222,11 @@
{0,},
};
-MODULE_DEVICE_TABLE(pci, orinoco_pci_pci_id_table);
+MODULE_DEVICE_TABLE(pci, orinoco_pci_id_table);
static struct pci_driver orinoco_pci_driver = {
.name = DRIVER_NAME,
- .id_table = orinoco_pci_pci_id_table,
+ .id_table = orinoco_pci_id_table,
.probe = orinoco_pci_init_one,
.remove = __devexit_p(orinoco_pci_remove_one),
.suspend = orinoco_pci_suspend,
diff --git a/drivers/net/wireless/orinoco_pci.h b/drivers/net/wireless/orinoco_pci.h
new file mode 100644
index 0000000..b05a9a5
--- /dev/null
+++ b/drivers/net/wireless/orinoco_pci.h
@@ -0,0 +1,125 @@
+/* orinoco_pci.h
+ *
+ * Common code for all Orinoco drivers for PCI devices, including
+ * both native PCI and PCMCIA-to-PCI bridges.
+ *
+ * Copyright (C) 2005, Pavel Roskin.
+ * See orinoco.c for license.
+ */
+
+#ifndef _ORINOCO_PCI_H
+#define _ORINOCO_PCI_H
+
+#include <linux/netdevice.h>
+
+/* Driver specific data */
+struct orinoco_pci_card {
+ void __iomem *bridge_io;
+ void __iomem *attr_io;
+};
+
+/* Set base address or memory range of the network device based on
+ * the PCI device it's using. Specify BAR of the "main" resource.
+ * To be used after request_irq(). */
+static inline void orinoco_pci_setup_netdev(struct net_device *dev,
+ struct pci_dev *pdev, int bar)
+{
+ char *range_type;
+ unsigned long start = pci_resource_start(pdev, bar);
+ unsigned long len = pci_resource_len(pdev, bar);
+ unsigned long flags = pci_resource_flags(pdev, bar);
+ unsigned long end = start + len - 1;
+
+ dev->irq = pdev->irq;
+ if (flags & IORESOURCE_IO) {
+ dev->base_addr = start;
+ range_type = "ports";
+ } else {
+ dev->mem_start = start;
+ dev->mem_end = end;
+ range_type = "memory";
+ }
+
+ printk(KERN_DEBUG PFX "%s: irq %d, %s 0x%lx-0x%lx\n",
+ pci_name(pdev), pdev->irq, range_type, start, end);
+}
+
+static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+ int err;
+
+ err = orinoco_lock(priv, &flags);
+ if (err) {
+ printk(KERN_ERR "%s: cannot lock hardware for suspend\n",
+ dev->name);
+ return err;
+ }
+
+ err = __orinoco_down(dev);
+ if (err)
+ printk(KERN_WARNING "%s: error %d bringing interface down "
+ "for suspend\n", dev->name, err);
+
+ netif_device_detach(dev);
+
+ priv->hw_unavailable++;
+
+ orinoco_unlock(priv, &flags);
+
+ free_irq(pdev->irq, dev);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
+}
+
+static int orinoco_pci_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+ int err;
+
+ pci_set_power_state(pdev, 0);
+ pci_enable_device(pdev);
+ pci_restore_state(pdev);
+
+ err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
+ dev->name, dev);
+ if (err) {
+ printk(KERN_ERR "%s: cannot re-allocate IRQ on resume\n",
+ dev->name);
+ pci_disable_device(pdev);
+ return -EBUSY;
+ }
+
+ err = orinoco_reinit_firmware(dev);
+ if (err) {
+ printk(KERN_ERR "%s: error %d re-initializing firmware "
+ "on resume\n", dev->name, err);
+ return err;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ netif_device_attach(dev);
+
+ priv->hw_unavailable--;
+
+ if (priv->open && (! priv->hw_unavailable)) {
+ err = __orinoco_up(dev);
+ if (err)
+ printk(KERN_ERR "%s: Error %d restarting card on resume\n",
+ dev->name, err);
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+#endif /* _ORINOCO_PCI_H */
diff --git a/drivers/net/wireless/orinoco_plx.c b/drivers/net/wireless/orinoco_plx.c
index 210e737..7b94050 100644
--- a/drivers/net/wireless/orinoco_plx.c
+++ b/drivers/net/wireless/orinoco_plx.c
@@ -3,7 +3,7 @@
* Driver for Prism II devices which would usually be driven by orinoco_cs,
* but are connected to the PCI bus by a PLX9052.
*
- * Current maintainers (as of 29 September 2003) are:
+ * Current maintainers are:
* Pavel Roskin <proski AT gnu.org>
* and David Gibson <hermes AT gibson.dropbear.id.au>
*
@@ -30,38 +30,18 @@
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
-
- * Caution: this is experimental and probably buggy. For success and
- * failure reports for different cards and adaptors, see
- * orinoco_plx_pci_id_table near the end of the file. If you have a
- * card we don't have the PCI id for, and looks like it should work,
- * drop me mail with the id and "it works"/"it doesn't work".
*
- * Note: if everything gets detected fine but it doesn't actually send
- * or receive packets, your first port of call should probably be to
- * try newer firmware in the card. Especially if you're doing Ad-Hoc
- * modes.
- *
- * The actual driving is done by orinoco.c, this is just resource
- * allocation stuff. The explanation below is courtesy of Ryan Niemi
- * on the linux-wlan-ng list at
- * http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html
- *
- * The PLX9052-based cards (WL11000 and several others) are a
- * different beast than the usual PCMCIA-based PRISM2 configuration
- * expected by wlan-ng. Here's the general details on how the WL11000
- * PCI adapter works:
+ * Here's the general details on how the PLX9052 adapter works:
*
* - Two PCI I/O address spaces, one 0x80 long which contains the
* PLX9052 registers, and one that's 0x40 long mapped to the PCMCIA
* slot I/O address space.
*
- * - One PCI memory address space, mapped to the PCMCIA memory space
+ * - One PCI memory address space, mapped to the PCMCIA attribute space
* (containing the CIS).
*
- * After identifying the I/O and memory space, you can read through
- * the memory space to confirm the CIS's device ID or manufacturer ID
- * to make sure it's the expected card. qKeep in mind that the PCMCIA
+ * Using the later, you can read through the CIS data to make sure the
+ * card is compatible with the driver. Keep in mind that the PCMCIA
* spec specifies the CIS as the lower 8 bits of each word read from
* the CIS, so to read the bytes of the CIS, read every other byte
* (0,2,4,...). Passing that test, you need to enable the I/O address
@@ -71,7 +51,7 @@
* within the PCI memory space. Write 0x41 to the COR register to
* enable I/O mode and to select level triggered interrupts. To
* confirm you actually succeeded, read the COR register back and make
- * sure it actually got set to 0x41, incase you have an unexpected
+ * sure it actually got set to 0x41, in case you have an unexpected
* card inserted.
*
* Following that, you can treat the second PCI I/O address space (the
@@ -101,16 +81,6 @@
* that, I've hot-swapped a number of times during debugging and
* driver development for various reasons (stuck WAIT# line after the
* radio card's firmware locks up).
- *
- * Hope this is enough info for someone to add PLX9052 support to the
- * wlan-ng card. In the case of the WL11000, the PCI ID's are
- * 0x1639/0x0200, with matching subsystem ID's. Other PLX9052-based
- * manufacturers other than Eumitcom (or on cards other than the
- * WL11000) may have different PCI ID's.
- *
- * If anyone needs any more specific info, let me know. I haven't had
- * time to implement support myself yet, and with the way things are
- * going, might not have time for a while..
*/
#define DRIVER_NAME "orinoco_plx"
@@ -125,6 +95,7 @@
#include <pcmcia/cisreg.h>
#include "orinoco.h"
+#include "orinoco_pci.h"
#define COR_OFFSET (0x3e0) /* COR attribute offset of Prism2 PC card */
#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
@@ -134,30 +105,20 @@
#define PLX_INTCSR 0x4c /* Interrupt Control & Status Register */
#define PLX_INTCSR_INTEN (1<<6) /* Interrupt Enable bit */
-static const u8 cis_magic[] = {
- 0x01, 0x03, 0x00, 0x00, 0xff, 0x17, 0x04, 0x67
-};
-
-/* Orinoco PLX specific data */
-struct orinoco_plx_card {
- void __iomem *attr_mem;
-};
-
/*
* Do a soft reset of the card using the Configuration Option Register
*/
static int orinoco_plx_cor_reset(struct orinoco_private *priv)
{
hermes_t *hw = &priv->hw;
- struct orinoco_plx_card *card = priv->card;
- u8 __iomem *attr_mem = card->attr_mem;
+ struct orinoco_pci_card *card = priv->card;
unsigned long timeout;
u16 reg;
- writeb(COR_VALUE | COR_RESET, attr_mem + COR_OFFSET);
+ iowrite8(COR_VALUE | COR_RESET, card->attr_io + COR_OFFSET);
mdelay(1);
- writeb(COR_VALUE, attr_mem + COR_OFFSET);
+ iowrite8(COR_VALUE, card->attr_io + COR_OFFSET);
mdelay(1);
/* Just in case, wait more until the card is no longer busy */
@@ -168,7 +129,7 @@
reg = hermes_read_regn(hw, CMD);
}
- /* Did we timeout ? */
+ /* Still busy? */
if (reg & HERMES_CMD_BUSY) {
printk(KERN_ERR PFX "Busy timeout\n");
return -ETIMEDOUT;
@@ -177,20 +138,55 @@
return 0;
}
+static int orinoco_plx_hw_init(struct orinoco_pci_card *card)
+{
+ int i;
+ u32 csr_reg;
+ static const u8 cis_magic[] = {
+ 0x01, 0x03, 0x00, 0x00, 0xff, 0x17, 0x04, 0x67
+ };
+
+ printk(KERN_DEBUG PFX "CIS: ");
+ for (i = 0; i < 16; i++) {
+ printk("%02X:", ioread8(card->attr_io + (i << 1)));
+ }
+ printk("\n");
+
+ /* Verify whether a supported PC card is present */
+ /* FIXME: we probably need to be smarted about this */
+ for (i = 0; i < sizeof(cis_magic); i++) {
+ if (cis_magic[i] != ioread8(card->attr_io + (i << 1))) {
+ printk(KERN_ERR PFX "The CIS value of Prism2 PC "
+ "card is unexpected\n");
+ return -ENODEV;
+ }
+ }
+
+ /* bjoern: We need to tell the card to enable interrupts, in
+ case the serial eprom didn't do this already. See the
+ PLX9052 data book, p8-1 and 8-24 for reference. */
+ csr_reg = ioread32(card->bridge_io + PLX_INTCSR);
+ if (!(csr_reg & PLX_INTCSR_INTEN)) {
+ csr_reg |= PLX_INTCSR_INTEN;
+ iowrite32(csr_reg, card->bridge_io + PLX_INTCSR);
+ csr_reg = ioread32(card->bridge_io + PLX_INTCSR);
+ if (!(csr_reg & PLX_INTCSR_INTEN)) {
+ printk(KERN_ERR PFX "Cannot enable interrupts\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
static int orinoco_plx_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- int err = 0;
- u8 __iomem *attr_mem = NULL;
- u32 csr_reg, plx_addr;
- struct orinoco_private *priv = NULL;
- struct orinoco_plx_card *card;
- unsigned long pccard_ioaddr = 0;
- unsigned long pccard_iolen = 0;
- struct net_device *dev = NULL;
- void __iomem *mem;
- int i;
+ int err;
+ struct orinoco_private *priv;
+ struct orinoco_pci_card *card;
+ struct net_device *dev;
+ void __iomem *hermes_io, *attr_io, *bridge_io;
err = pci_enable_device(pdev);
if (err) {
@@ -199,30 +195,30 @@
}
err = pci_request_regions(pdev, DRIVER_NAME);
- if (err != 0) {
+ if (err) {
printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
goto fail_resources;
}
- /* Resource 1 is mapped to PLX-specific registers */
- plx_addr = pci_resource_start(pdev, 1);
+ bridge_io = pci_iomap(pdev, 1, 0);
+ if (!bridge_io) {
+ printk(KERN_ERR PFX "Cannot map bridge registers\n");
+ err = -EIO;
+ goto fail_map_bridge;
+ }
- /* Resource 2 is mapped to the PCMCIA attribute memory */
- attr_mem = ioremap(pci_resource_start(pdev, 2),
- pci_resource_len(pdev, 2));
- if (!attr_mem) {
- printk(KERN_ERR PFX "Cannot remap PCMCIA space\n");
+ attr_io = pci_iomap(pdev, 2, 0);
+ if (!attr_io) {
+ printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n");
+ err = -EIO;
goto fail_map_attr;
}
- /* Resource 3 is mapped to the PCMCIA I/O address space */
- pccard_ioaddr = pci_resource_start(pdev, 3);
- pccard_iolen = pci_resource_len(pdev, 3);
-
- mem = pci_iomap(pdev, 3, 0);
- if (!mem) {
- err = -ENOMEM;
- goto fail_map_io;
+ hermes_io = pci_iomap(pdev, 3, 0);
+ if (!hermes_io) {
+ printk(KERN_ERR PFX "Cannot map chipset registers\n");
+ err = -EIO;
+ goto fail_map_hermes;
}
/* Allocate network device */
@@ -235,16 +231,12 @@
priv = netdev_priv(dev);
card = priv->card;
- card->attr_mem = attr_mem;
- dev->base_addr = pccard_ioaddr;
+ card->bridge_io = bridge_io;
+ card->attr_io = attr_io;
SET_MODULE_OWNER(dev);
SET_NETDEV_DEV(dev, &pdev->dev);
- hermes_struct_init(&priv->hw, mem, HERMES_16BIT_REGSPACING);
-
- printk(KERN_DEBUG PFX "Detected Orinoco/Prism2 PLX device "
- "at %s irq:%d, io addr:0x%lx\n", pci_name(pdev), pdev->irq,
- pccard_ioaddr);
+ hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING);
err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
dev->name, dev);
@@ -253,20 +245,12 @@
err = -EBUSY;
goto fail_irq;
}
- dev->irq = pdev->irq;
+ orinoco_pci_setup_netdev(dev, pdev, 2);
- /* bjoern: We need to tell the card to enable interrupts, in
- case the serial eprom didn't do this already. See the
- PLX9052 data book, p8-1 and 8-24 for reference. */
- csr_reg = inl(plx_addr + PLX_INTCSR);
- if (!(csr_reg & PLX_INTCSR_INTEN)) {
- csr_reg |= PLX_INTCSR_INTEN;
- outl(csr_reg, plx_addr + PLX_INTCSR);
- csr_reg = inl(plx_addr + PLX_INTCSR);
- if (!(csr_reg & PLX_INTCSR_INTEN)) {
- printk(KERN_ERR PFX "Cannot enable interrupts\n");
- goto fail;
- }
+ err = orinoco_plx_hw_init(card);
+ if (err) {
+ printk(KERN_ERR PFX "Hardware initialization failed\n");
+ goto fail;
}
err = orinoco_plx_cor_reset(priv);
@@ -275,23 +259,6 @@
goto fail;
}
- printk(KERN_DEBUG PFX "CIS: ");
- for (i = 0; i < 16; i++) {
- printk("%02X:", readb(attr_mem + 2*i));
- }
- printk("\n");
-
- /* Verify whether a supported PC card is present */
- /* FIXME: we probably need to be smarted about this */
- for (i = 0; i < sizeof(cis_magic); i++) {
- if (cis_magic[i] != readb(attr_mem +2*i)) {
- printk(KERN_ERR PFX "The CIS value of Prism2 PC "
- "card is unexpected\n");
- err = -EIO;
- goto fail;
- }
- }
-
err = register_netdev(dev);
if (err) {
printk(KERN_ERR PFX "Cannot register network device\n");
@@ -310,12 +277,15 @@
free_orinocodev(dev);
fail_alloc:
- pci_iounmap(pdev, mem);
+ pci_iounmap(pdev, hermes_io);
- fail_map_io:
- iounmap(attr_mem);
+ fail_map_hermes:
+ pci_iounmap(pdev, attr_io);
fail_map_attr:
+ pci_iounmap(pdev, bridge_io);
+
+ fail_map_bridge:
pci_release_regions(pdev);
fail_resources:
@@ -328,23 +298,20 @@
{
struct net_device *dev = pci_get_drvdata(pdev);
struct orinoco_private *priv = netdev_priv(dev);
- struct orinoco_plx_card *card = priv->card;
- u8 __iomem *attr_mem = card->attr_mem;
-
- BUG_ON(! dev);
+ struct orinoco_pci_card *card = priv->card;
unregister_netdev(dev);
free_irq(dev->irq, dev);
pci_set_drvdata(pdev, NULL);
free_orinocodev(dev);
pci_iounmap(pdev, priv->hw.iobase);
- iounmap(attr_mem);
+ pci_iounmap(pdev, card->attr_io);
+ pci_iounmap(pdev, card->bridge_io);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
-
-static struct pci_device_id orinoco_plx_pci_id_table[] = {
+static struct pci_device_id orinoco_plx_id_table[] = {
{0x111a, 0x1023, PCI_ANY_ID, PCI_ANY_ID,}, /* Siemens SpeedStream SS1023 */
{0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */
{0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga - does this work? */
@@ -362,13 +329,15 @@
{0,},
};
-MODULE_DEVICE_TABLE(pci, orinoco_plx_pci_id_table);
+MODULE_DEVICE_TABLE(pci, orinoco_plx_id_table);
static struct pci_driver orinoco_plx_driver = {
.name = DRIVER_NAME,
- .id_table = orinoco_plx_pci_id_table,
+ .id_table = orinoco_plx_id_table,
.probe = orinoco_plx_init_one,
.remove = __devexit_p(orinoco_plx_remove_one),
+ .suspend = orinoco_pci_suspend,
+ .resume = orinoco_pci_resume,
};
static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
@@ -388,7 +357,6 @@
static void __exit orinoco_plx_exit(void)
{
pci_unregister_driver(&orinoco_plx_driver);
- ssleep(1);
}
module_init(orinoco_plx_init);
diff --git a/drivers/net/wireless/orinoco_tmd.c b/drivers/net/wireless/orinoco_tmd.c
index 5e68b70..0496663 100644
--- a/drivers/net/wireless/orinoco_tmd.c
+++ b/drivers/net/wireless/orinoco_tmd.c
@@ -1,5 +1,5 @@
/* orinoco_tmd.c
- *
+ *
* Driver for Prism II devices which would usually be driven by orinoco_cs,
* but are connected to the PCI bus by a TMD7160.
*
@@ -26,25 +26,13 @@
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
-
- * Caution: this is experimental and probably buggy. For success and
- * failure reports for different cards and adaptors, see
- * orinoco_tmd_pci_id_table near the end of the file. If you have a
- * card we don't have the PCI id for, and looks like it should work,
- * drop me mail with the id and "it works"/"it doesn't work".
- *
- * Note: if everything gets detected fine but it doesn't actually send
- * or receive packets, your first port of call should probably be to
- * try newer firmware in the card. Especially if you're doing Ad-Hoc
- * modes
*
* The actual driving is done by orinoco.c, this is just resource
* allocation stuff.
*
* This driver is modeled after the orinoco_plx driver. The main
- * difference is that the TMD chip has only IO port ranges and no
- * memory space, i.e. no access to the CIS. Compared to the PLX chip,
- * the io range functionalities are exchanged.
+ * difference is that the TMD chip has only IO port ranges and doesn't
+ * provide access to the PCMCIA attribute space.
*
* Pheecom sells cards with the TMD chip as "ASIC version"
*/
@@ -61,32 +49,26 @@
#include <pcmcia/cisreg.h>
#include "orinoco.h"
+#include "orinoco_pci.h"
#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
#define COR_RESET (0x80) /* reset bit in the COR register */
#define TMD_RESET_TIME (500) /* milliseconds */
-/* Orinoco TMD specific data */
-struct orinoco_tmd_card {
- u32 tmd_io;
-};
-
-
/*
* Do a soft reset of the card using the Configuration Option Register
*/
static int orinoco_tmd_cor_reset(struct orinoco_private *priv)
{
hermes_t *hw = &priv->hw;
- struct orinoco_tmd_card *card = priv->card;
- u32 addr = card->tmd_io;
+ struct orinoco_pci_card *card = priv->card;
unsigned long timeout;
u16 reg;
- outb(COR_VALUE | COR_RESET, addr);
+ iowrite8(COR_VALUE | COR_RESET, card->bridge_io);
mdelay(1);
- outb(COR_VALUE, addr);
+ iowrite8(COR_VALUE, card->bridge_io);
mdelay(1);
/* Just in case, wait more until the card is no longer busy */
@@ -97,7 +79,7 @@
reg = hermes_read_regn(hw, CMD);
}
- /* Did we timeout ? */
+ /* Still busy? */
if (reg & HERMES_CMD_BUSY) {
printk(KERN_ERR PFX "Busy timeout\n");
return -ETIMEDOUT;
@@ -110,11 +92,11 @@
static int orinoco_tmd_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- int err = 0;
- struct orinoco_private *priv = NULL;
- struct orinoco_tmd_card *card;
- struct net_device *dev = NULL;
- void __iomem *mem;
+ int err;
+ struct orinoco_private *priv;
+ struct orinoco_pci_card *card;
+ struct net_device *dev;
+ void __iomem *hermes_io, *bridge_io;
err = pci_enable_device(pdev);
if (err) {
@@ -123,20 +105,28 @@
}
err = pci_request_regions(pdev, DRIVER_NAME);
- if (err != 0) {
+ if (err) {
printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
goto fail_resources;
}
- mem = pci_iomap(pdev, 2, 0);
- if (! mem) {
- err = -ENOMEM;
- goto fail_iomap;
+ bridge_io = pci_iomap(pdev, 1, 0);
+ if (!bridge_io) {
+ printk(KERN_ERR PFX "Cannot map bridge registers\n");
+ err = -EIO;
+ goto fail_map_bridge;
+ }
+
+ hermes_io = pci_iomap(pdev, 2, 0);
+ if (!hermes_io) {
+ printk(KERN_ERR PFX "Cannot map chipset registers\n");
+ err = -EIO;
+ goto fail_map_hermes;
}
/* Allocate network device */
dev = alloc_orinocodev(sizeof(*card), orinoco_tmd_cor_reset);
- if (! dev) {
+ if (!dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
err = -ENOMEM;
goto fail_alloc;
@@ -144,16 +134,11 @@
priv = netdev_priv(dev);
card = priv->card;
- card->tmd_io = pci_resource_start(pdev, 1);
- dev->base_addr = pci_resource_start(pdev, 2);
+ card->bridge_io = bridge_io;
SET_MODULE_OWNER(dev);
SET_NETDEV_DEV(dev, &pdev->dev);
- hermes_struct_init(&priv->hw, mem, HERMES_16BIT_REGSPACING);
-
- printk(KERN_DEBUG PFX "Detected Orinoco/Prism2 TMD device "
- "at %s irq:%d, io addr:0x%lx\n", pci_name(pdev), pdev->irq,
- dev->base_addr);
+ hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING);
err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
dev->name, dev);
@@ -162,7 +147,7 @@
err = -EBUSY;
goto fail_irq;
}
- dev->irq = pdev->irq;
+ orinoco_pci_setup_netdev(dev, pdev, 2);
err = orinoco_tmd_cor_reset(priv);
if (err) {
@@ -188,9 +173,12 @@
free_orinocodev(dev);
fail_alloc:
- pci_iounmap(pdev, mem);
+ pci_iounmap(pdev, hermes_io);
- fail_iomap:
+ fail_map_hermes:
+ pci_iounmap(pdev, bridge_io);
+
+ fail_map_bridge:
pci_release_regions(pdev);
fail_resources:
@@ -203,31 +191,32 @@
{
struct net_device *dev = pci_get_drvdata(pdev);
struct orinoco_private *priv = dev->priv;
-
- BUG_ON(! dev);
+ struct orinoco_pci_card *card = priv->card;
unregister_netdev(dev);
free_irq(dev->irq, dev);
pci_set_drvdata(pdev, NULL);
free_orinocodev(dev);
pci_iounmap(pdev, priv->hw.iobase);
+ pci_iounmap(pdev, card->bridge_io);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
-
-static struct pci_device_id orinoco_tmd_pci_id_table[] = {
+static struct pci_device_id orinoco_tmd_id_table[] = {
{0x15e8, 0x0131, PCI_ANY_ID, PCI_ANY_ID,}, /* NDC and OEMs, e.g. pheecom */
{0,},
};
-MODULE_DEVICE_TABLE(pci, orinoco_tmd_pci_id_table);
+MODULE_DEVICE_TABLE(pci, orinoco_tmd_id_table);
static struct pci_driver orinoco_tmd_driver = {
.name = DRIVER_NAME,
- .id_table = orinoco_tmd_pci_id_table,
+ .id_table = orinoco_tmd_id_table,
.probe = orinoco_tmd_init_one,
.remove = __devexit_p(orinoco_tmd_remove_one),
+ .suspend = orinoco_pci_suspend,
+ .resume = orinoco_pci_resume,
};
static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
@@ -245,7 +234,6 @@
static void __exit orinoco_tmd_exit(void)
{
pci_unregister_driver(&orinoco_tmd_driver);
- ssleep(1);
}
module_init(orinoco_tmd_init);
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c
index f7b77ce..aeb38d9 100644
--- a/drivers/net/wireless/spectrum_cs.c
+++ b/drivers/net/wireless/spectrum_cs.c
@@ -1,6 +1,6 @@
/*
* Driver for 802.11b cards using RAM-loadable Symbol firmware, such as
- * Symbol Wireless Networker LA4100, CompactFlash cards by Socket
+ * Symbol Wireless Networker LA4137, CompactFlash cards by Socket
* Communications and Intel PRO/Wireless 2011B.
*
* The driver implements Symbol firmware download. The rest is handled
@@ -120,8 +120,8 @@
* Each block has the following structure.
*/
struct dblock {
- __le32 _addr; /* adapter address where to write the block */
- __le16 _len; /* length of the data only, in bytes */
+ __le32 addr; /* adapter address where to write the block */
+ __le16 len; /* length of the data only, in bytes */
char data[0]; /* data to be written */
} __attribute__ ((packed));
@@ -131,9 +131,9 @@
* items with matching ID should be written.
*/
struct pdr {
- __le32 _id; /* record ID */
- __le32 _addr; /* adapter address where to write the data */
- __le32 _len; /* expected length of the data, in bytes */
+ __le32 id; /* record ID */
+ __le32 addr; /* adapter address where to write the data */
+ __le32 len; /* expected length of the data, in bytes */
char next[0]; /* next PDR starts here */
} __attribute__ ((packed));
@@ -144,8 +144,8 @@
* be plugged into the secondary firmware.
*/
struct pdi {
- __le16 _len; /* length of ID and data, in words */
- __le16 _id; /* record ID */
+ __le16 len; /* length of ID and data, in words */
+ __le16 id; /* record ID */
char data[0]; /* plug data */
} __attribute__ ((packed));
@@ -154,44 +154,44 @@
static inline u32
dblock_addr(const struct dblock *blk)
{
- return le32_to_cpu(blk->_addr);
+ return le32_to_cpu(blk->addr);
}
static inline u32
dblock_len(const struct dblock *blk)
{
- return le16_to_cpu(blk->_len);
+ return le16_to_cpu(blk->len);
}
static inline u32
pdr_id(const struct pdr *pdr)
{
- return le32_to_cpu(pdr->_id);
+ return le32_to_cpu(pdr->id);
}
static inline u32
pdr_addr(const struct pdr *pdr)
{
- return le32_to_cpu(pdr->_addr);
+ return le32_to_cpu(pdr->addr);
}
static inline u32
pdr_len(const struct pdr *pdr)
{
- return le32_to_cpu(pdr->_len);
+ return le32_to_cpu(pdr->len);
}
static inline u32
pdi_id(const struct pdi *pdi)
{
- return le16_to_cpu(pdi->_id);
+ return le16_to_cpu(pdi->id);
}
/* Return length of the data only, in bytes */
static inline u32
pdi_len(const struct pdi *pdi)
{
- return 2 * (le16_to_cpu(pdi->_len) - 1);
+ return 2 * (le16_to_cpu(pdi->len) - 1);
}
@@ -343,8 +343,7 @@
/* do the actual plugging */
spectrum_aux_setaddr(hw, pdr_addr(pdr));
- hermes_write_words(hw, HERMES_AUXDATA, pdi->data,
- pdi_len(pdi) / 2);
+ hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
return 0;
}
@@ -424,8 +423,8 @@
while (dblock_addr(blk) != BLOCK_END) {
spectrum_aux_setaddr(hw, blkaddr);
- hermes_write_words(hw, HERMES_AUXDATA, blk->data,
- blklen / 2);
+ hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
+ blklen);
blk = (struct dblock *) &blk->data[blklen];
blkaddr = dblock_addr(blk);
@@ -653,13 +652,10 @@
int last_fn, last_ret;
u_char buf[64];
config_info_t conf;
- cisinfo_t info;
tuple_t tuple;
cisparse_t parse;
void __iomem *mem;
- CS_CHECK(ValidateCIS, pcmcia_validate_cis(link, &info));
-
/*
* This reads the card's CONFIG tuple to find its
* configuration registers.
@@ -709,12 +705,6 @@
goto next_entry;
link->conf.ConfigIndex = cfg->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)) {
@@ -932,7 +922,7 @@
" David Gibson <hermes@gibson.dropbear.id.au>, et al)";
static struct pcmcia_device_id spectrum_cs_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4100 */
+ PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */
PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */
PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */
PCMCIA_DEVICE_NULL,
diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile
index 90d4d0e..6775a83 100644
--- a/drivers/s390/net/Makefile
+++ b/drivers/s390/net/Makefile
@@ -2,7 +2,7 @@
# S/390 network devices
#
-ctc-objs := ctcmain.o ctctty.o ctcdbug.o
+ctc-objs := ctcmain.o ctcdbug.o
obj-$(CONFIG_IUCV) += iucv.o
obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o
@@ -10,6 +10,7 @@
obj-$(CONFIG_CTC) += ctc.o fsm.o cu3088.o
obj-$(CONFIG_LCS) += lcs.o cu3088.o
obj-$(CONFIG_CLAW) += claw.o cu3088.o
+obj-$(CONFIG_MPC) += ctcmpc.o fsm.o cu3088.o
qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o qeth_eddp.o
qeth-$(CONFIG_PROC_FS) += qeth_proc.o
obj-$(CONFIG_QETH) += qeth.o
diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c
index af9f212..e2ccaf5 100644
--- a/drivers/s390/net/ctcmain.c
+++ b/drivers/s390/net/ctcmain.c
@@ -6,7 +6,7 @@
* Fixes by : Jochen Röhrig (roehrig@de.ibm.com)
* Arnaldo Carvalho de Melo <acme@conectiva.com.br>
Peter Tiedemann (ptiedem@de.ibm.com)
- * Driver Model stuff by : Cornelia Huck <huckc@de.ibm.com>
+ * Driver Model stuff by : Cornelia Huck <cornelia.huck@de.ibm.com>
*
* Documentation used:
* - Principles of Operation (IBM doc#: SA22-7201-06)
@@ -65,7 +65,6 @@
#include <asm/idals.h>
-#include "ctctty.h"
#include "fsm.h"
#include "cu3088.h"
@@ -479,10 +478,7 @@
skb->dev = pskb->dev;
skb->protocol = pskb->protocol;
pskb->ip_summed = CHECKSUM_UNNECESSARY;
- if (ch->protocol == CTC_PROTO_LINUX_TTY)
- ctc_tty_netif_rx(skb);
- else
- netif_rx_ni(skb);
+ netif_rx_ni(skb);
/**
* Successful rx; reset logflags
*/
@@ -557,8 +553,7 @@
DBF_TEXT(trace, 5, __FUNCTION__);
if (sense & SNS0_INTERVENTION_REQ) {
if (sense & 0x01) {
- if (ch->protocol != CTC_PROTO_LINUX_TTY)
- ctc_pr_debug("%s: Interface disc. or Sel. reset "
+ ctc_pr_debug("%s: Interface disc. or Sel. reset "
"(remote)\n", ch->id);
fsm_event(ch->fsm, CH_EVENT_UC_RCRESET, ch);
} else {
@@ -2034,7 +2029,6 @@
dev_action_chup(fsm_instance * fi, int event, void *arg)
{
struct net_device *dev = (struct net_device *) arg;
- struct ctc_priv *privptr = dev->priv;
DBF_TEXT(trace, 3, __FUNCTION__);
switch (fsm_getstate(fi)) {
@@ -2049,8 +2043,6 @@
fsm_newstate(fi, DEV_STATE_RUNNING);
ctc_pr_info("%s: connected with remote side\n",
dev->name);
- if (privptr->protocol == CTC_PROTO_LINUX_TTY)
- ctc_tty_setcarrier(dev, 1);
ctc_clear_busy(dev);
}
break;
@@ -2059,8 +2051,6 @@
fsm_newstate(fi, DEV_STATE_RUNNING);
ctc_pr_info("%s: connected with remote side\n",
dev->name);
- if (privptr->protocol == CTC_PROTO_LINUX_TTY)
- ctc_tty_setcarrier(dev, 1);
ctc_clear_busy(dev);
}
break;
@@ -2086,14 +2076,10 @@
static void
dev_action_chdown(fsm_instance * fi, int event, void *arg)
{
- struct net_device *dev = (struct net_device *) arg;
- struct ctc_priv *privptr = dev->priv;
DBF_TEXT(trace, 3, __FUNCTION__);
switch (fsm_getstate(fi)) {
case DEV_STATE_RUNNING:
- if (privptr->protocol == CTC_PROTO_LINUX_TTY)
- ctc_tty_setcarrier(dev, 0);
if (event == DEV_EVENT_TXDOWN)
fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
else
@@ -2397,8 +2383,6 @@
*/
if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
fsm_event(privptr->fsm, DEV_EVENT_START, dev);
- if (privptr->protocol == CTC_PROTO_LINUX_TTY)
- return -EBUSY;
dev_kfree_skb(skb);
privptr->stats.tx_dropped++;
privptr->stats.tx_errors++;
@@ -2608,20 +2592,13 @@
if (!dev)
return;
privptr = (struct ctc_priv *) dev->priv;
- if (privptr->protocol != CTC_PROTO_LINUX_TTY)
- unregister_netdev(dev);
- else
- ctc_tty_unregister_netdev(dev);
+ unregister_netdev(dev);
}
static int
ctc_netdev_register(struct net_device * dev)
{
- struct ctc_priv *privptr = (struct ctc_priv *) dev->priv;
- if (privptr->protocol != CTC_PROTO_LINUX_TTY)
- return register_netdev(dev);
- else
- return ctc_tty_register_netdev(dev);
+ return register_netdev(dev);
}
static void
@@ -2667,7 +2644,9 @@
if (!priv)
return -ENODEV;
sscanf(buf, "%u", &value);
- if ((value < 0) || (value > CTC_PROTO_MAX))
+ if (!((value == CTC_PROTO_S390) ||
+ (value == CTC_PROTO_LINUX) ||
+ (value == CTC_PROTO_OS390)))
return -EINVAL;
priv->protocol = value;
@@ -2897,10 +2876,7 @@
goto out;
}
- if (privptr->protocol == CTC_PROTO_LINUX_TTY)
- strlcpy(dev->name, "ctctty%d", IFNAMSIZ);
- else
- strlcpy(dev->name, "ctc%d", IFNAMSIZ);
+ strlcpy(dev->name, "ctc%d", IFNAMSIZ);
for (direction = READ; direction <= WRITE; direction++) {
privptr->channel[direction] =
@@ -3046,7 +3022,6 @@
{
DBF_TEXT(setup, 3, __FUNCTION__);
unregister_cu3088_discipline(&ctc_group_driver);
- ctc_tty_cleanup();
ctc_unregister_dbf_views();
ctc_pr_info("CTC driver unloaded\n");
}
@@ -3073,10 +3048,8 @@
ctc_pr_crit("ctc_init failed with ctc_register_dbf_views rc = %d\n", ret);
return ret;
}
- ctc_tty_init();
ret = register_cu3088_discipline(&ctc_group_driver);
if (ret) {
- ctc_tty_cleanup();
ctc_unregister_dbf_views();
}
return ret;
diff --git a/drivers/s390/net/ctcmain.h b/drivers/s390/net/ctcmain.h
index d2e835c..7f305d1 100644
--- a/drivers/s390/net/ctcmain.h
+++ b/drivers/s390/net/ctcmain.h
@@ -35,7 +35,9 @@
#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>
-#include "ctctty.h"
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
#include "fsm.h"
#include "cu3088.h"
@@ -50,9 +52,7 @@
#define CTC_PROTO_S390 0
#define CTC_PROTO_LINUX 1
-#define CTC_PROTO_LINUX_TTY 2
#define CTC_PROTO_OS390 3
-#define CTC_PROTO_MAX 3
#define CTC_BUFSIZE_LIMIT 65535
#define CTC_BUFSIZE_DEFAULT 32768
@@ -257,15 +257,13 @@
ctc_clear_busy(struct net_device * dev)
{
clear_bit(0, &(((struct ctc_priv *) dev->priv)->tbusy));
- if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY)
- netif_wake_queue(dev);
+ netif_wake_queue(dev);
}
static __inline__ int
ctc_test_and_set_busy(struct net_device * dev)
{
- if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY)
- netif_stop_queue(dev);
+ netif_stop_queue(dev);
return test_and_set_bit(0, &((struct ctc_priv *) dev->priv)->tbusy);
}
diff --git a/drivers/s390/net/ctctty.c b/drivers/s390/net/ctctty.c
deleted file mode 100644
index 5cdcdbf..0000000
--- a/drivers/s390/net/ctctty.c
+++ /dev/null
@@ -1,1259 +0,0 @@
-/*
- * CTC / ESCON network driver, tty interface.
- *
- * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
- *
- * 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, 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.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial_reg.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <asm/uaccess.h>
-#include <linux/devfs_fs_kernel.h>
-#include "ctctty.h"
-#include "ctcdbug.h"
-
-#define CTC_TTY_MAJOR 43
-#define CTC_TTY_MAX_DEVICES 64
-
-#define CTC_ASYNC_MAGIC 0x49344C01 /* for paranoia-checking */
-#define CTC_ASYNC_INITIALIZED 0x80000000 /* port was initialized */
-#define CTC_ASYNC_NORMAL_ACTIVE 0x20000000 /* Normal device active */
-#define CTC_ASYNC_CLOSING 0x08000000 /* Serial port is closing */
-#define CTC_ASYNC_CTS_FLOW 0x04000000 /* Do CTS flow control */
-#define CTC_ASYNC_CHECK_CD 0x02000000 /* i.e., CLOCAL */
-#define CTC_ASYNC_HUP_NOTIFY 0x0001 /* Notify tty on hangups/closes */
-#define CTC_ASYNC_NETDEV_OPEN 0x0002 /* Underlying netdev is open */
-#define CTC_ASYNC_TX_LINESTAT 0x0004 /* Must send line status */
-#define CTC_ASYNC_SPLIT_TERMIOS 0x0008 /* Sep. termios for dialin/out */
-#define CTC_TTY_XMIT_SIZE 1024 /* Default bufsize for write */
-#define CTC_SERIAL_XMIT_MAX 4000 /* Maximum bufsize for write */
-
-/* Private data (similar to async_struct in <linux/serial.h>) */
-typedef struct {
- int magic;
- int flags; /* defined in tty.h */
- int mcr; /* Modem control register */
- int msr; /* Modem status register */
- int lsr; /* Line status register */
- int line;
- int count; /* # of fd on device */
- int blocked_open; /* # of blocked opens */
- struct net_device *netdev;
- struct sk_buff_head tx_queue; /* transmit queue */
- struct sk_buff_head rx_queue; /* receive queue */
- struct tty_struct *tty; /* Pointer to corresponding tty */
- wait_queue_head_t open_wait;
- wait_queue_head_t close_wait;
- struct semaphore write_sem;
- struct tasklet_struct tasklet;
- struct timer_list stoptimer;
-} ctc_tty_info;
-
-/* Description of one CTC-tty */
-typedef struct {
- struct tty_driver *ctc_tty_device; /* tty-device */
- ctc_tty_info info[CTC_TTY_MAX_DEVICES]; /* Private data */
-} ctc_tty_driver;
-
-static ctc_tty_driver *driver;
-
-/* Leave this unchanged unless you know what you do! */
-#define MODEM_PARANOIA_CHECK
-#define MODEM_DO_RESTART
-
-#define CTC_TTY_NAME "ctctty"
-
-static __u32 ctc_tty_magic = CTC_ASYNC_MAGIC;
-static int ctc_tty_shuttingdown = 0;
-
-static spinlock_t ctc_tty_lock;
-
-/* ctc_tty_try_read() is called from within ctc_tty_rcv_skb()
- * to stuff incoming data directly into a tty's flip-buffer. If the
- * flip buffer is full, the packet gets queued up.
- *
- * Return:
- * 1 = Success
- * 0 = Failure, data has to be buffered and later processed by
- * ctc_tty_readmodem().
- */
-static int
-ctc_tty_try_read(ctc_tty_info * info, struct sk_buff *skb)
-{
- int len;
- struct tty_struct *tty;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- if ((tty = info->tty)) {
- if (info->mcr & UART_MCR_RTS) {
- len = skb->len;
- tty_insert_flip_string(tty, skb->data, len);
- tty_flip_buffer_push(tty);
- kfree_skb(skb);
- return 1;
- }
- }
- return 0;
-}
-
-/* ctc_tty_readmodem() is called periodically from within timer-interrupt.
- * It tries getting received data from the receive queue an stuff it into
- * the tty's flip-buffer.
- */
-static int
-ctc_tty_readmodem(ctc_tty_info *info)
-{
- int ret = 1;
- struct tty_struct *tty;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- if ((tty = info->tty)) {
- if (info->mcr & UART_MCR_RTS) {
- struct sk_buff *skb;
-
- if ((skb = skb_dequeue(&info->rx_queue))) {
- int len = skb->len;
- tty_insert_flip_string(tty, skb->data, len);
- skb_pull(skb, len);
- tty_flip_buffer_push(tty);
- if (skb->len > 0)
- skb_queue_head(&info->rx_queue, skb);
- else {
- kfree_skb(skb);
- ret = !skb_queue_empty(&info->rx_queue);
- }
- }
- }
- }
- return ret;
-}
-
-void
-ctc_tty_setcarrier(struct net_device *netdev, int on)
-{
- int i;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if ((!driver) || ctc_tty_shuttingdown)
- return;
- for (i = 0; i < CTC_TTY_MAX_DEVICES; i++)
- if (driver->info[i].netdev == netdev) {
- ctc_tty_info *info = &driver->info[i];
- if (on)
- info->msr |= UART_MSR_DCD;
- else
- info->msr &= ~UART_MSR_DCD;
- if ((info->flags & CTC_ASYNC_CHECK_CD) && (!on))
- tty_hangup(info->tty);
- }
-}
-
-void
-ctc_tty_netif_rx(struct sk_buff *skb)
-{
- int i;
- ctc_tty_info *info = NULL;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- if (!skb)
- return;
- if ((!skb->dev) || (!driver) || ctc_tty_shuttingdown) {
- dev_kfree_skb(skb);
- return;
- }
- for (i = 0; i < CTC_TTY_MAX_DEVICES; i++)
- if (driver->info[i].netdev == skb->dev) {
- info = &driver->info[i];
- break;
- }
- if (!info) {
- dev_kfree_skb(skb);
- return;
- }
- if (skb->len < 6) {
- dev_kfree_skb(skb);
- return;
- }
- if (memcmp(skb->data, &ctc_tty_magic, sizeof(__u32))) {
- dev_kfree_skb(skb);
- return;
- }
- skb_pull(skb, sizeof(__u32));
-
- i = *((int *)skb->data);
- skb_pull(skb, sizeof(info->mcr));
- if (i & UART_MCR_RTS) {
- info->msr |= UART_MSR_CTS;
- if (info->flags & CTC_ASYNC_CTS_FLOW)
- info->tty->hw_stopped = 0;
- } else {
- info->msr &= ~UART_MSR_CTS;
- if (info->flags & CTC_ASYNC_CTS_FLOW)
- info->tty->hw_stopped = 1;
- }
- if (i & UART_MCR_DTR)
- info->msr |= UART_MSR_DSR;
- else
- info->msr &= ~UART_MSR_DSR;
- if (skb->len <= 0) {
- kfree_skb(skb);
- return;
- }
- /* Try to deliver directly via tty-flip-buf if queue is empty */
- if (skb_queue_empty(&info->rx_queue))
- if (ctc_tty_try_read(info, skb))
- return;
- /* Direct deliver failed or queue wasn't empty.
- * Queue up for later dequeueing via timer-irq.
- */
- skb_queue_tail(&info->rx_queue, skb);
- /* Schedule dequeuing */
- tasklet_schedule(&info->tasklet);
-}
-
-static int
-ctc_tty_tint(ctc_tty_info * info)
-{
- struct sk_buff *skb = skb_dequeue(&info->tx_queue);
- int stopped = (info->tty->hw_stopped || info->tty->stopped);
- int wake = 1;
- int rc;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (!info->netdev) {
- if (skb)
- kfree_skb(skb);
- return 0;
- }
- if (info->flags & CTC_ASYNC_TX_LINESTAT) {
- int skb_res = info->netdev->hard_header_len +
- sizeof(info->mcr) + sizeof(__u32);
- /* If we must update line status,
- * create an empty dummy skb and insert it.
- */
- if (skb)
- skb_queue_head(&info->tx_queue, skb);
-
- skb = dev_alloc_skb(skb_res);
- if (!skb) {
- printk(KERN_WARNING
- "ctc_tty: Out of memory in %s%d tint\n",
- CTC_TTY_NAME, info->line);
- return 1;
- }
- skb_reserve(skb, skb_res);
- stopped = 0;
- wake = 0;
- }
- if (!skb)
- return 0;
- if (stopped) {
- skb_queue_head(&info->tx_queue, skb);
- return 1;
- }
-#if 0
- if (skb->len > 0)
- printk(KERN_DEBUG "tint: %d %02x\n", skb->len, *(skb->data));
- else
- printk(KERN_DEBUG "tint: %d STAT\n", skb->len);
-#endif
- memcpy(skb_push(skb, sizeof(info->mcr)), &info->mcr, sizeof(info->mcr));
- memcpy(skb_push(skb, sizeof(__u32)), &ctc_tty_magic, sizeof(__u32));
- rc = info->netdev->hard_start_xmit(skb, info->netdev);
- if (rc) {
- skb_pull(skb, sizeof(info->mcr) + sizeof(__u32));
- if (skb->len > 0)
- skb_queue_head(&info->tx_queue, skb);
- else
- kfree_skb(skb);
- } else {
- struct tty_struct *tty = info->tty;
-
- info->flags &= ~CTC_ASYNC_TX_LINESTAT;
- if (tty) {
- tty_wakeup(tty);
- }
- }
- return (skb_queue_empty(&info->tx_queue) ? 0 : 1);
-}
-
-/************************************************************
- *
- * Modem-functions
- *
- * mostly "stolen" from original Linux-serial.c and friends.
- *
- ************************************************************/
-
-static inline int
-ctc_tty_paranoia_check(ctc_tty_info * info, char *name, const char *routine)
-{
-#ifdef MODEM_PARANOIA_CHECK
- if (!info) {
- printk(KERN_WARNING "ctc_tty: null info_struct for %s in %s\n",
- name, routine);
- return 1;
- }
- if (info->magic != CTC_ASYNC_MAGIC) {
- printk(KERN_WARNING "ctc_tty: bad magic for info struct %s in %s\n",
- name, routine);
- return 1;
- }
-#endif
- return 0;
-}
-
-static void
-ctc_tty_inject(ctc_tty_info *info, char c)
-{
- int skb_res;
- struct sk_buff *skb;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (ctc_tty_shuttingdown)
- return;
- skb_res = info->netdev->hard_header_len + sizeof(info->mcr) +
- sizeof(__u32) + 1;
- skb = dev_alloc_skb(skb_res);
- if (!skb) {
- printk(KERN_WARNING
- "ctc_tty: Out of memory in %s%d tx_inject\n",
- CTC_TTY_NAME, info->line);
- return;
- }
- skb_reserve(skb, skb_res);
- *(skb_put(skb, 1)) = c;
- skb_queue_head(&info->tx_queue, skb);
- tasklet_schedule(&info->tasklet);
-}
-
-static void
-ctc_tty_transmit_status(ctc_tty_info *info)
-{
- DBF_TEXT(trace, 5, __FUNCTION__);
- if (ctc_tty_shuttingdown)
- return;
- info->flags |= CTC_ASYNC_TX_LINESTAT;
- tasklet_schedule(&info->tasklet);
-}
-
-static void
-ctc_tty_change_speed(ctc_tty_info * info)
-{
- unsigned int cflag;
- unsigned int quot;
- int i;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- if (!info->tty || !info->tty->termios)
- return;
- cflag = info->tty->termios->c_cflag;
-
- quot = i = cflag & CBAUD;
- if (i & CBAUDEX) {
- i &= ~CBAUDEX;
- if (i < 1 || i > 2)
- info->tty->termios->c_cflag &= ~CBAUDEX;
- else
- i += 15;
- }
- if (quot) {
- info->mcr |= UART_MCR_DTR;
- info->mcr |= UART_MCR_RTS;
- ctc_tty_transmit_status(info);
- } else {
- info->mcr &= ~UART_MCR_DTR;
- info->mcr &= ~UART_MCR_RTS;
- ctc_tty_transmit_status(info);
- return;
- }
-
- /* CTS flow control flag and modem status interrupts */
- if (cflag & CRTSCTS) {
- info->flags |= CTC_ASYNC_CTS_FLOW;
- } else
- info->flags &= ~CTC_ASYNC_CTS_FLOW;
- if (cflag & CLOCAL)
- info->flags &= ~CTC_ASYNC_CHECK_CD;
- else {
- info->flags |= CTC_ASYNC_CHECK_CD;
- }
-}
-
-static int
-ctc_tty_startup(ctc_tty_info * info)
-{
- DBF_TEXT(trace, 3, __FUNCTION__);
- if (info->flags & CTC_ASYNC_INITIALIZED)
- return 0;
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "starting up %s%d ...\n", CTC_TTY_NAME, info->line);
-#endif
- /*
- * Now, initialize the UART
- */
- info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
- if (info->tty)
- clear_bit(TTY_IO_ERROR, &info->tty->flags);
- /*
- * and set the speed of the serial port
- */
- ctc_tty_change_speed(info);
-
- info->flags |= CTC_ASYNC_INITIALIZED;
- if (!(info->flags & CTC_ASYNC_NETDEV_OPEN))
- info->netdev->open(info->netdev);
- info->flags |= CTC_ASYNC_NETDEV_OPEN;
- return 0;
-}
-
-static void
-ctc_tty_stopdev(unsigned long data)
-{
- ctc_tty_info *info = (ctc_tty_info *)data;
-
- if ((!info) || (!info->netdev) ||
- (info->flags & CTC_ASYNC_INITIALIZED))
- return;
- info->netdev->stop(info->netdev);
- info->flags &= ~CTC_ASYNC_NETDEV_OPEN;
-}
-
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void
-ctc_tty_shutdown(ctc_tty_info * info)
-{
- DBF_TEXT(trace, 3, __FUNCTION__);
- if (!(info->flags & CTC_ASYNC_INITIALIZED))
- return;
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "Shutting down %s%d ....\n", CTC_TTY_NAME, info->line);
-#endif
- info->msr &= ~UART_MSR_RI;
- if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
- info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
- if (info->tty)
- set_bit(TTY_IO_ERROR, &info->tty->flags);
- mod_timer(&info->stoptimer, jiffies + (10 * HZ));
- skb_queue_purge(&info->tx_queue);
- skb_queue_purge(&info->rx_queue);
- info->flags &= ~CTC_ASYNC_INITIALIZED;
-}
-
-/* ctc_tty_write() is the main send-routine. It is called from the upper
- * levels within the kernel to perform sending data. Depending on the
- * online-flag it either directs output to the at-command-interpreter or
- * to the lower level. Additional tasks done here:
- * - If online, check for escape-sequence (+++)
- * - If sending audio-data, call ctc_tty_DLEdown() to parse DLE-codes.
- * - If receiving audio-data, call ctc_tty_end_vrx() to abort if needed.
- * - If dialing, abort dial.
- */
-static int
-ctc_tty_write(struct tty_struct *tty, const u_char * buf, int count)
-{
- int c;
- int total = 0;
- ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- if (ctc_tty_shuttingdown)
- goto ex;
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_write"))
- goto ex;
- if (!tty)
- goto ex;
- if (!info->netdev) {
- total = -ENODEV;
- goto ex;
- }
- while (1) {
- struct sk_buff *skb;
- int skb_res;
-
- c = (count < CTC_TTY_XMIT_SIZE) ? count : CTC_TTY_XMIT_SIZE;
- if (c <= 0)
- break;
-
- skb_res = info->netdev->hard_header_len + sizeof(info->mcr) +
- + sizeof(__u32);
- skb = dev_alloc_skb(skb_res + c);
- if (!skb) {
- printk(KERN_WARNING
- "ctc_tty: Out of memory in %s%d write\n",
- CTC_TTY_NAME, info->line);
- break;
- }
- skb_reserve(skb, skb_res);
- memcpy(skb_put(skb, c), buf, c);
- skb_queue_tail(&info->tx_queue, skb);
- buf += c;
- total += c;
- count -= c;
- }
- if (!skb_queue_empty(&info->tx_queue)) {
- info->lsr &= ~UART_LSR_TEMT;
- tasklet_schedule(&info->tasklet);
- }
-ex:
- DBF_TEXT(trace, 6, __FUNCTION__);
- return total;
-}
-
-static int
-ctc_tty_write_room(struct tty_struct *tty)
-{
- ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
-
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_write_room"))
- return 0;
- return CTC_TTY_XMIT_SIZE;
-}
-
-static int
-ctc_tty_chars_in_buffer(struct tty_struct *tty)
-{
- ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
-
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_chars_in_buffer"))
- return 0;
- return 0;
-}
-
-static void
-ctc_tty_flush_buffer(struct tty_struct *tty)
-{
- ctc_tty_info *info;
- unsigned long flags;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (!tty)
- goto ex;
- spin_lock_irqsave(&ctc_tty_lock, flags);
- info = (ctc_tty_info *) tty->driver_data;
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_flush_buffer")) {
- spin_unlock_irqrestore(&ctc_tty_lock, flags);
- goto ex;
- }
- skb_queue_purge(&info->tx_queue);
- info->lsr |= UART_LSR_TEMT;
- spin_unlock_irqrestore(&ctc_tty_lock, flags);
- wake_up_interruptible(&tty->write_wait);
- tty_wakeup(tty);
-ex:
- DBF_TEXT_(trace, 2, "ex: %s ", __FUNCTION__);
- return;
-}
-
-static void
-ctc_tty_flush_chars(struct tty_struct *tty)
-{
- ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (ctc_tty_shuttingdown)
- return;
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_flush_chars"))
- return;
- if (tty->stopped || tty->hw_stopped || skb_queue_empty(&info->tx_queue))
- return;
- tasklet_schedule(&info->tasklet);
-}
-
-/*
- * ------------------------------------------------------------
- * ctc_tty_throttle()
- *
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void
-ctc_tty_throttle(struct tty_struct *tty)
-{
- ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_throttle"))
- return;
- info->mcr &= ~UART_MCR_RTS;
- if (I_IXOFF(tty))
- ctc_tty_inject(info, STOP_CHAR(tty));
- ctc_tty_transmit_status(info);
-}
-
-static void
-ctc_tty_unthrottle(struct tty_struct *tty)
-{
- ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_unthrottle"))
- return;
- info->mcr |= UART_MCR_RTS;
- if (I_IXOFF(tty))
- ctc_tty_inject(info, START_CHAR(tty));
- ctc_tty_transmit_status(info);
-}
-
-/*
- * ------------------------------------------------------------
- * ctc_tty_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-/*
- * ctc_tty_get_lsr_info - get line status register info
- *
- * Purpose: Let user call ioctl() to get info when the UART physically
- * is emptied. On bus types like RS485, the transmitter must
- * release the bus after transmitting. This must be done when
- * the transmit shift register is empty, not be done when the
- * transmit holding register is empty. This functionality
- * allows RS485 driver to be written in user space.
- */
-static int
-ctc_tty_get_lsr_info(ctc_tty_info * info, uint __user *value)
-{
- u_char status;
- uint result;
- ulong flags;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- spin_lock_irqsave(&ctc_tty_lock, flags);
- status = info->lsr;
- spin_unlock_irqrestore(&ctc_tty_lock, flags);
- result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
- put_user(result, value);
- return 0;
-}
-
-
-static int ctc_tty_tiocmget(struct tty_struct *tty, struct file *file)
-{
- ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
- u_char control,
- status;
- uint result;
- ulong flags;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_ioctl"))
- return -ENODEV;
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
-
- control = info->mcr;
- spin_lock_irqsave(&ctc_tty_lock, flags);
- status = info->msr;
- spin_unlock_irqrestore(&ctc_tty_lock, flags);
- result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
- | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
- | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
- | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
- | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
- | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
- return result;
-}
-
-static int
-ctc_tty_tiocmset(struct tty_struct *tty, struct file *file,
- unsigned int set, unsigned int clear)
-{
- ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_ioctl"))
- return -ENODEV;
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
-
- if (set & TIOCM_RTS)
- info->mcr |= UART_MCR_RTS;
- if (set & TIOCM_DTR)
- info->mcr |= UART_MCR_DTR;
-
- if (clear & TIOCM_RTS)
- info->mcr &= ~UART_MCR_RTS;
- if (clear & TIOCM_DTR)
- info->mcr &= ~UART_MCR_DTR;
-
- if ((set | clear) & (TIOCM_RTS|TIOCM_DTR))
- ctc_tty_transmit_status(info);
- return 0;
-}
-
-static int
-ctc_tty_ioctl(struct tty_struct *tty, struct file *file,
- uint cmd, ulong arg)
-{
- ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
- int error;
- int retval;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_ioctl"))
- return -ENODEV;
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
- switch (cmd) {
- case TCSBRK: /* SVID version: non-zero arg --> no break */
-#ifdef CTC_DEBUG_MODEM_IOCTL
- printk(KERN_DEBUG "%s%d ioctl TCSBRK\n", CTC_TTY_NAME, info->line);
-#endif
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- return 0;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
-#ifdef CTC_DEBUG_MODEM_IOCTL
- printk(KERN_DEBUG "%s%d ioctl TCSBRKP\n", CTC_TTY_NAME, info->line);
-#endif
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- return 0;
- case TIOCGSOFTCAR:
-#ifdef CTC_DEBUG_MODEM_IOCTL
- printk(KERN_DEBUG "%s%d ioctl TIOCGSOFTCAR\n", CTC_TTY_NAME,
- info->line);
-#endif
- error = put_user(C_CLOCAL(tty) ? 1 : 0, (ulong __user *) arg);
- return error;
- case TIOCSSOFTCAR:
-#ifdef CTC_DEBUG_MODEM_IOCTL
- printk(KERN_DEBUG "%s%d ioctl TIOCSSOFTCAR\n", CTC_TTY_NAME,
- info->line);
-#endif
- error = get_user(arg, (ulong __user *) arg);
- if (error)
- return error;
- tty->termios->c_cflag =
- ((tty->termios->c_cflag & ~CLOCAL) |
- (arg ? CLOCAL : 0));
- return 0;
- case TIOCSERGETLSR: /* Get line status register */
-#ifdef CTC_DEBUG_MODEM_IOCTL
- printk(KERN_DEBUG "%s%d ioctl TIOCSERGETLSR\n", CTC_TTY_NAME,
- info->line);
-#endif
- if (access_ok(VERIFY_WRITE, (void __user *) arg, sizeof(uint)))
- return ctc_tty_get_lsr_info(info, (uint __user *) arg);
- else
- return -EFAULT;
- default:
-#ifdef CTC_DEBUG_MODEM_IOCTL
- printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on %s%d\n", cmd,
- CTC_TTY_NAME, info->line);
-#endif
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static void
-ctc_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
-{
- ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
- unsigned int cflag = tty->termios->c_cflag;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- ctc_tty_change_speed(info);
-
- /* Handle transition to B0 */
- if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) {
- info->mcr &= ~(UART_MCR_DTR|UART_MCR_RTS);
- ctc_tty_transmit_status(info);
- }
-
- /* Handle transition from B0 to other */
- if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
- info->mcr |= UART_MCR_DTR;
- if (!(tty->termios->c_cflag & CRTSCTS) ||
- !test_bit(TTY_THROTTLED, &tty->flags)) {
- info->mcr |= UART_MCR_RTS;
- }
- ctc_tty_transmit_status(info);
- }
-
- /* Handle turning off CRTSCTS */
- if ((old_termios->c_cflag & CRTSCTS) &&
- !(tty->termios->c_cflag & CRTSCTS))
- tty->hw_stopped = 0;
-}
-
-/*
- * ------------------------------------------------------------
- * ctc_tty_open() and friends
- * ------------------------------------------------------------
- */
-static int
-ctc_tty_block_til_ready(struct tty_struct *tty, struct file *filp, ctc_tty_info *info)
-{
- DECLARE_WAITQUEUE(wait, NULL);
- int do_clocal = 0;
- unsigned long flags;
- int retval;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- /*
- * If the device is in the middle of being closed, then block
- * until it's done, and then try again.
- */
- if (tty_hung_up_p(filp) ||
- (info->flags & CTC_ASYNC_CLOSING)) {
- if (info->flags & CTC_ASYNC_CLOSING)
- wait_event(info->close_wait,
- !(info->flags & CTC_ASYNC_CLOSING));
-#ifdef MODEM_DO_RESTART
- if (info->flags & CTC_ASYNC_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
-#else
- return -EAGAIN;
-#endif
- }
- /*
- * If non-blocking mode is set, then make the check up front
- * and then exit.
- */
- if ((filp->f_flags & O_NONBLOCK) ||
- (tty->flags & (1 << TTY_IO_ERROR))) {
- info->flags |= CTC_ASYNC_NORMAL_ACTIVE;
- return 0;
- }
- if (tty->termios->c_cflag & CLOCAL)
- do_clocal = 1;
- /*
- * Block waiting for the carrier detect and the line to become
- * free (i.e., not in use by the callout). While we are in
- * this loop, info->count is dropped by one, so that
- * ctc_tty_close() knows when to free things. We restore it upon
- * exit, either normal or abnormal.
- */
- retval = 0;
- add_wait_queue(&info->open_wait, &wait);
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "ctc_tty_block_til_ready before block: %s%d, count = %d\n",
- CTC_TTY_NAME, info->line, info->count);
-#endif
- spin_lock_irqsave(&ctc_tty_lock, flags);
- if (!(tty_hung_up_p(filp)))
- info->count--;
- spin_unlock_irqrestore(&ctc_tty_lock, flags);
- info->blocked_open++;
- while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (tty_hung_up_p(filp) ||
- !(info->flags & CTC_ASYNC_INITIALIZED)) {
-#ifdef MODEM_DO_RESTART
- if (info->flags & CTC_ASYNC_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
-#else
- retval = -EAGAIN;
-#endif
- break;
- }
- if (!(info->flags & CTC_ASYNC_CLOSING) &&
- (do_clocal || (info->msr & UART_MSR_DCD))) {
- break;
- }
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "ctc_tty_block_til_ready blocking: %s%d, count = %d\n",
- CTC_TTY_NAME, info->line, info->count);
-#endif
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&info->open_wait, &wait);
- if (!tty_hung_up_p(filp))
- info->count++;
- info->blocked_open--;
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "ctc_tty_block_til_ready after blocking: %s%d, count = %d\n",
- CTC_TTY_NAME, info->line, info->count);
-#endif
- if (retval)
- return retval;
- info->flags |= CTC_ASYNC_NORMAL_ACTIVE;
- return 0;
-}
-
-/*
- * This routine is called whenever a serial port is opened. It
- * enables interrupts for a serial port, linking in its async structure into
- * the IRQ chain. It also performs the serial-specific
- * initialization for the tty structure.
- */
-static int
-ctc_tty_open(struct tty_struct *tty, struct file *filp)
-{
- ctc_tty_info *info;
- unsigned long saveflags;
- int retval,
- line;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- line = tty->index;
- if (line < 0 || line > CTC_TTY_MAX_DEVICES)
- return -ENODEV;
- info = &driver->info[line];
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_open"))
- return -ENODEV;
- if (!info->netdev)
- return -ENODEV;
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "ctc_tty_open %s, count = %d\n", tty->name,
- info->count);
-#endif
- spin_lock_irqsave(&ctc_tty_lock, saveflags);
- info->count++;
- tty->driver_data = info;
- info->tty = tty;
- spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
- /*
- * Start up serial port
- */
- retval = ctc_tty_startup(info);
- if (retval) {
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "ctc_tty_open return after startup\n");
-#endif
- return retval;
- }
- retval = ctc_tty_block_til_ready(tty, filp, info);
- if (retval) {
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "ctc_tty_open return after ctc_tty_block_til_ready \n");
-#endif
- return retval;
- }
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "ctc_tty_open %s successful...\n", tty->name);
-#endif
- return 0;
-}
-
-static void
-ctc_tty_close(struct tty_struct *tty, struct file *filp)
-{
- ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
- ulong flags;
- ulong timeout;
- DBF_TEXT(trace, 3, __FUNCTION__);
- if (!info || ctc_tty_paranoia_check(info, tty->name, "ctc_tty_close"))
- return;
- spin_lock_irqsave(&ctc_tty_lock, flags);
- if (tty_hung_up_p(filp)) {
- spin_unlock_irqrestore(&ctc_tty_lock, flags);
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "ctc_tty_close return after tty_hung_up_p\n");
-#endif
- return;
- }
- if ((tty->count == 1) && (info->count != 1)) {
- /*
- * Uh, oh. tty->count is 1, which means that the tty
- * structure will be freed. Info->count should always
- * be one in these conditions. If it's greater than
- * one, we've got real problems, since it means the
- * serial port won't be shutdown.
- */
- printk(KERN_ERR "ctc_tty_close: bad port count; tty->count is 1, "
- "info->count is %d\n", info->count);
- info->count = 1;
- }
- if (--info->count < 0) {
- printk(KERN_ERR "ctc_tty_close: bad port count for %s%d: %d\n",
- CTC_TTY_NAME, info->line, info->count);
- info->count = 0;
- }
- if (info->count) {
- local_irq_restore(flags);
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "ctc_tty_close after info->count != 0\n");
-#endif
- return;
- }
- info->flags |= CTC_ASYNC_CLOSING;
- tty->closing = 1;
- /*
- * At this point we stop accepting input. To do this, we
- * disable the receive line status interrupts, and tell the
- * interrupt driver to stop checking the data ready bit in the
- * line status register.
- */
- if (info->flags & CTC_ASYNC_INITIALIZED) {
- tty_wait_until_sent(tty, 30*HZ); /* 30 seconds timeout */
- /*
- * Before we drop DTR, make sure the UART transmitter
- * has completely drained; this is especially
- * important if there is a transmit FIFO!
- */
- timeout = jiffies + HZ;
- while (!(info->lsr & UART_LSR_TEMT)) {
- spin_unlock_irqrestore(&ctc_tty_lock, flags);
- msleep(500);
- spin_lock_irqsave(&ctc_tty_lock, flags);
- if (time_after(jiffies,timeout))
- break;
- }
- }
- ctc_tty_shutdown(info);
- if (tty->driver->flush_buffer) {
- skb_queue_purge(&info->tx_queue);
- info->lsr |= UART_LSR_TEMT;
- }
- tty_ldisc_flush(tty);
- info->tty = 0;
- tty->closing = 0;
- if (info->blocked_open) {
- msleep_interruptible(500);
- wake_up_interruptible(&info->open_wait);
- }
- info->flags &= ~(CTC_ASYNC_NORMAL_ACTIVE | CTC_ASYNC_CLOSING);
- wake_up_interruptible(&info->close_wait);
- spin_unlock_irqrestore(&ctc_tty_lock, flags);
-#ifdef CTC_DEBUG_MODEM_OPEN
- printk(KERN_DEBUG "ctc_tty_close normal exit\n");
-#endif
-}
-
-/*
- * ctc_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-static void
-ctc_tty_hangup(struct tty_struct *tty)
-{
- ctc_tty_info *info = (ctc_tty_info *)tty->driver_data;
- unsigned long saveflags;
- DBF_TEXT(trace, 3, __FUNCTION__);
- if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_hangup"))
- return;
- ctc_tty_shutdown(info);
- info->count = 0;
- info->flags &= ~CTC_ASYNC_NORMAL_ACTIVE;
- spin_lock_irqsave(&ctc_tty_lock, saveflags);
- info->tty = 0;
- spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
- wake_up_interruptible(&info->open_wait);
-}
-
-
-/*
- * For all online tty's, try sending data to
- * the lower levels.
- */
-static void
-ctc_tty_task(unsigned long arg)
-{
- ctc_tty_info *info = (void *)arg;
- unsigned long saveflags;
- int again;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- spin_lock_irqsave(&ctc_tty_lock, saveflags);
- if ((!ctc_tty_shuttingdown) && info) {
- again = ctc_tty_tint(info);
- if (!again)
- info->lsr |= UART_LSR_TEMT;
- again |= ctc_tty_readmodem(info);
- if (again) {
- tasklet_schedule(&info->tasklet);
- }
- }
- spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
-}
-
-static struct tty_operations ctc_ops = {
- .open = ctc_tty_open,
- .close = ctc_tty_close,
- .write = ctc_tty_write,
- .flush_chars = ctc_tty_flush_chars,
- .write_room = ctc_tty_write_room,
- .chars_in_buffer = ctc_tty_chars_in_buffer,
- .flush_buffer = ctc_tty_flush_buffer,
- .ioctl = ctc_tty_ioctl,
- .throttle = ctc_tty_throttle,
- .unthrottle = ctc_tty_unthrottle,
- .set_termios = ctc_tty_set_termios,
- .hangup = ctc_tty_hangup,
- .tiocmget = ctc_tty_tiocmget,
- .tiocmset = ctc_tty_tiocmset,
-};
-
-int
-ctc_tty_init(void)
-{
- int i;
- ctc_tty_info *info;
- struct tty_driver *device;
-
- DBF_TEXT(trace, 2, __FUNCTION__);
- driver = kmalloc(sizeof(ctc_tty_driver), GFP_KERNEL);
- if (driver == NULL) {
- printk(KERN_WARNING "Out of memory in ctc_tty_modem_init\n");
- return -ENOMEM;
- }
- memset(driver, 0, sizeof(ctc_tty_driver));
- device = alloc_tty_driver(CTC_TTY_MAX_DEVICES);
- if (!device) {
- kfree(driver);
- printk(KERN_WARNING "Out of memory in ctc_tty_modem_init\n");
- return -ENOMEM;
- }
-
- device->devfs_name = "ctc/" CTC_TTY_NAME;
- device->name = CTC_TTY_NAME;
- device->major = CTC_TTY_MAJOR;
- device->minor_start = 0;
- device->type = TTY_DRIVER_TYPE_SERIAL;
- device->subtype = SERIAL_TYPE_NORMAL;
- device->init_termios = tty_std_termios;
- device->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- device->flags = TTY_DRIVER_REAL_RAW;
- device->driver_name = "ctc_tty",
- tty_set_operations(device, &ctc_ops);
- if (tty_register_driver(device)) {
- printk(KERN_WARNING "ctc_tty: Couldn't register serial-device\n");
- put_tty_driver(device);
- kfree(driver);
- return -1;
- }
- driver->ctc_tty_device = device;
- for (i = 0; i < CTC_TTY_MAX_DEVICES; i++) {
- info = &driver->info[i];
- init_MUTEX(&info->write_sem);
- tasklet_init(&info->tasklet, ctc_tty_task,
- (unsigned long) info);
- info->magic = CTC_ASYNC_MAGIC;
- info->line = i;
- info->tty = 0;
- info->count = 0;
- info->blocked_open = 0;
- init_waitqueue_head(&info->open_wait);
- init_waitqueue_head(&info->close_wait);
- skb_queue_head_init(&info->tx_queue);
- skb_queue_head_init(&info->rx_queue);
- init_timer(&info->stoptimer);
- info->stoptimer.function = ctc_tty_stopdev;
- info->stoptimer.data = (unsigned long)info;
- info->mcr = UART_MCR_RTS;
- }
- return 0;
-}
-
-int
-ctc_tty_register_netdev(struct net_device *dev) {
- int ttynum;
- char *err;
- char *p;
-
- DBF_TEXT(trace, 2, __FUNCTION__);
- if ((!dev) || (!dev->name)) {
- printk(KERN_WARNING
- "ctc_tty_register_netdev called "
- "with NULL dev or NULL dev-name\n");
- return -1;
- }
-
- /*
- * If the name is a format string the caller wants us to
- * do a name allocation : format string must end with %d
- */
- if (strchr(dev->name, '%'))
- {
- int err = dev_alloc_name(dev, dev->name); // dev->name is changed by this
- if (err < 0) {
- printk(KERN_DEBUG "dev_alloc returned error %d\n", err);
- return err;
- }
-
- }
-
- for (p = dev->name; p && ((*p < '0') || (*p > '9')); p++);
- ttynum = simple_strtoul(p, &err, 0);
- if ((ttynum < 0) || (ttynum >= CTC_TTY_MAX_DEVICES) ||
- (err && *err)) {
- printk(KERN_WARNING
- "ctc_tty_register_netdev called "
- "with number in name '%s'\n", dev->name);
- return -1;
- }
- if (driver->info[ttynum].netdev) {
- printk(KERN_WARNING
- "ctc_tty_register_netdev called "
- "for already registered device '%s'\n",
- dev->name);
- return -1;
- }
- driver->info[ttynum].netdev = dev;
- return 0;
-}
-
-void
-ctc_tty_unregister_netdev(struct net_device *dev) {
- int i;
- unsigned long saveflags;
- ctc_tty_info *info = NULL;
-
- DBF_TEXT(trace, 2, __FUNCTION__);
- spin_lock_irqsave(&ctc_tty_lock, saveflags);
- for (i = 0; i < CTC_TTY_MAX_DEVICES; i++)
- if (driver->info[i].netdev == dev) {
- info = &driver->info[i];
- break;
- }
- if (info) {
- info->netdev = NULL;
- skb_queue_purge(&info->tx_queue);
- skb_queue_purge(&info->rx_queue);
- }
- spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
-}
-
-void
-ctc_tty_cleanup(void) {
- unsigned long saveflags;
-
- DBF_TEXT(trace, 2, __FUNCTION__);
- spin_lock_irqsave(&ctc_tty_lock, saveflags);
- ctc_tty_shuttingdown = 1;
- spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
- tty_unregister_driver(driver->ctc_tty_device);
- put_tty_driver(driver->ctc_tty_device);
- kfree(driver);
- driver = NULL;
-}
diff --git a/drivers/s390/net/ctctty.h b/drivers/s390/net/ctctty.h
deleted file mode 100644
index 7254dc0..0000000
--- a/drivers/s390/net/ctctty.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * CTC / ESCON network driver, tty interface.
- *
- * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
- *
- * 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, 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.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _CTCTTY_H_
-#define _CTCTTY_H_
-
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-
-extern int ctc_tty_register_netdev(struct net_device *);
-extern void ctc_tty_unregister_netdev(struct net_device *);
-extern void ctc_tty_netif_rx(struct sk_buff *);
-extern int ctc_tty_init(void);
-extern void ctc_tty_cleanup(void);
-extern void ctc_tty_setcarrier(struct net_device *, int);
-
-#endif
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index d6fe048..563eead 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -935,6 +935,7 @@
#define PCI_DEVICE_ID_PLX_DJINN_ITOO 0x1151
#define PCI_DEVICE_ID_PLX_R753 0x1152
#define PCI_DEVICE_ID_PLX_OLITEC 0x1187
+#define PCI_DEVICE_ID_PLX_PCI200SYN 0x3196
#define PCI_DEVICE_ID_PLX_9050 0x9050
#define PCI_DEVICE_ID_PLX_9080 0x9080
#define PCI_DEVICE_ID_PLX_GTEK_SERIAL2 0xa001
diff --git a/include/net/ieee80211.h b/include/net/ieee80211.h
index 4725ff8..4087dfc 100644
--- a/include/net/ieee80211.h
+++ b/include/net/ieee80211.h
@@ -29,7 +29,7 @@
#include <linux/kernel.h> /* ARRAY_SIZE */
#include <linux/wireless.h>
-#define IEEE80211_VERSION "git-1.1.7"
+#define IEEE80211_VERSION "git-1.1.13"
#define IEEE80211_DATA_LEN 2304
/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
@@ -104,6 +104,9 @@
#define IEEE80211_SCTL_FRAG 0x000F
#define IEEE80211_SCTL_SEQ 0xFFF0
+/* QOS control */
+#define IEEE80211_QCTL_TID 0x000F
+
/* debug macros */
#ifdef CONFIG_IEEE80211_DEBUG
@@ -1073,6 +1076,7 @@
int (*handle_management) (struct net_device * dev,
struct ieee80211_network * network, u16 type);
+ int (*is_qos_active) (struct net_device *dev, struct sk_buff *skb);
/* Typical STA methods */
int (*handle_auth) (struct net_device * dev,
diff --git a/include/net/ieee80211softmac_wx.h b/include/net/ieee80211softmac_wx.h
index 3e0be45..4ee3ad5 100644
--- a/include/net/ieee80211softmac_wx.h
+++ b/include/net/ieee80211softmac_wx.h
@@ -91,4 +91,9 @@
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra);
+extern int
+ieee80211softmac_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra);
#endif /* _IEEE80211SOFTMAC_WX */
diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c
index 93def94..3fa5df2 100644
--- a/net/ieee80211/ieee80211_crypt_tkip.c
+++ b/net/ieee80211/ieee80211_crypt_tkip.c
@@ -501,8 +501,11 @@
static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
{
struct ieee80211_hdr_4addr *hdr11;
+ u16 stype;
hdr11 = (struct ieee80211_hdr_4addr *)skb->data;
+ stype = WLAN_FC_GET_STYPE(le16_to_cpu(hdr11->frame_ctl));
+
switch (le16_to_cpu(hdr11->frame_ctl) &
(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
case IEEE80211_FCTL_TODS:
@@ -523,7 +526,13 @@
break;
}
- hdr[12] = 0; /* priority */
+ if (stype & IEEE80211_STYPE_QOS_DATA) {
+ const struct ieee80211_hdr_3addrqos *qoshdr =
+ (struct ieee80211_hdr_3addrqos *)skb->data;
+ hdr[12] = le16_to_cpu(qoshdr->qos_ctl) & IEEE80211_QCTL_TID;
+ } else
+ hdr[12] = 0; /* priority */
+
hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
}
diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c
index 604b7b0..2bf567f 100644
--- a/net/ieee80211/ieee80211_rx.c
+++ b/net/ieee80211/ieee80211_rx.c
@@ -369,7 +369,6 @@
/* Put this code here so that we avoid duplicating it in all
* Rx paths. - Jean II */
-#ifdef CONFIG_WIRELESS_EXT
#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
/* If spy monitoring on */
if (ieee->spy_data.spy_number > 0) {
@@ -398,7 +397,6 @@
wireless_spy_update(ieee->dev, hdr->addr2, &wstats);
}
#endif /* IW_WIRELESS_SPY */
-#endif /* CONFIG_WIRELESS_EXT */
#ifdef NOT_YET
hostap_update_rx_stats(local->ap, hdr, rx_stats);
@@ -1692,8 +1690,8 @@
WLAN_FC_GET_STYPE(le16_to_cpu
(header->frame_ctl)));
- IEEE80211_WARNING("%s: IEEE80211_REASSOC_REQ received\n",
- ieee->dev->name);
+ IEEE80211_DEBUG_MGMT("%s: IEEE80211_REASSOC_REQ received\n",
+ ieee->dev->name);
if (ieee->handle_reassoc_request != NULL)
ieee->handle_reassoc_request(ieee->dev,
(struct ieee80211_reassoc_request *)
@@ -1705,8 +1703,8 @@
WLAN_FC_GET_STYPE(le16_to_cpu
(header->frame_ctl)));
- IEEE80211_WARNING("%s: IEEE80211_ASSOC_REQ received\n",
- ieee->dev->name);
+ IEEE80211_DEBUG_MGMT("%s: IEEE80211_ASSOC_REQ received\n",
+ ieee->dev->name);
if (ieee->handle_assoc_request != NULL)
ieee->handle_assoc_request(ieee->dev);
break;
@@ -1722,10 +1720,10 @@
IEEE80211_DEBUG_MGMT("received UNKNOWN (%d)\n",
WLAN_FC_GET_STYPE(le16_to_cpu
(header->frame_ctl)));
- IEEE80211_WARNING("%s: Unknown management packet: %d\n",
- ieee->dev->name,
- WLAN_FC_GET_STYPE(le16_to_cpu
- (header->frame_ctl)));
+ IEEE80211_DEBUG_MGMT("%s: Unknown management packet: %d\n",
+ ieee->dev->name,
+ WLAN_FC_GET_STYPE(le16_to_cpu
+ (header->frame_ctl)));
break;
}
}
diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c
index 8b4332f..233d527 100644
--- a/net/ieee80211/ieee80211_tx.c
+++ b/net/ieee80211/ieee80211_tx.c
@@ -220,13 +220,43 @@
return txb;
}
+static int ieee80211_classify(struct sk_buff *skb)
+{
+ struct ethhdr *eth;
+ struct iphdr *ip;
+
+ eth = (struct ethhdr *)skb->data;
+ if (eth->h_proto != __constant_htons(ETH_P_IP))
+ return 0;
+
+ ip = skb->nh.iph;
+ switch (ip->tos & 0xfc) {
+ case 0x20:
+ return 2;
+ case 0x40:
+ return 1;
+ case 0x60:
+ return 3;
+ case 0x80:
+ return 4;
+ case 0xa0:
+ return 5;
+ case 0xc0:
+ return 6;
+ case 0xe0:
+ return 7;
+ default:
+ return 0;
+ }
+}
+
/* Incoming skb is converted to a txb which consists of
* a block of 802.11 fragment packets (stored as skbs) */
int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ieee80211_device *ieee = netdev_priv(dev);
struct ieee80211_txb *txb = NULL;
- struct ieee80211_hdr_3addr *frag_hdr;
+ struct ieee80211_hdr_3addrqos *frag_hdr;
int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
rts_required;
unsigned long flags;
@@ -234,9 +264,10 @@
int ether_type, encrypt, host_encrypt, host_encrypt_msdu, host_build_iv;
int bytes, fc, hdr_len;
struct sk_buff *skb_frag;
- struct ieee80211_hdr_3addr header = { /* Ensure zero initialized */
+ struct ieee80211_hdr_3addrqos header = {/* Ensure zero initialized */
.duration_id = 0,
- .seq_ctl = 0
+ .seq_ctl = 0,
+ .qos_ctl = 0
};
u8 dest[ETH_ALEN], src[ETH_ALEN];
struct ieee80211_crypt_data *crypt;
@@ -282,12 +313,6 @@
memcpy(dest, skb->data, ETH_ALEN);
memcpy(src, skb->data + ETH_ALEN, ETH_ALEN);
- /* Advance the SKB to the start of the payload */
- skb_pull(skb, sizeof(struct ethhdr));
-
- /* Determine total amount of storage required for TXB packets */
- bytes = skb->len + SNAP_SIZE + sizeof(u16);
-
if (host_encrypt || host_build_iv)
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
IEEE80211_FCTL_PROTECTED;
@@ -306,9 +331,23 @@
memcpy(header.addr2, src, ETH_ALEN);
memcpy(header.addr3, ieee->bssid, ETH_ALEN);
}
- header.frame_ctl = cpu_to_le16(fc);
hdr_len = IEEE80211_3ADDR_LEN;
+ if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) {
+ fc |= IEEE80211_STYPE_QOS_DATA;
+ hdr_len += 2;
+
+ skb->priority = ieee80211_classify(skb);
+ header.qos_ctl |= skb->priority & IEEE80211_QCTL_TID;
+ }
+ header.frame_ctl = cpu_to_le16(fc);
+
+ /* Advance the SKB to the start of the payload */
+ skb_pull(skb, sizeof(struct ethhdr));
+
+ /* Determine total amount of storage required for TXB packets */
+ bytes = skb->len + SNAP_SIZE + sizeof(u16);
+
/* Encrypt msdu first on the whole data packet. */
if ((host_encrypt || host_encrypt_msdu) &&
crypt && crypt->ops && crypt->ops->encrypt_msdu) {
@@ -402,7 +441,7 @@
if (rts_required) {
skb_frag = txb->fragments[0];
frag_hdr =
- (struct ieee80211_hdr_3addr *)skb_put(skb_frag, hdr_len);
+ (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
/*
* Set header frame_ctl to the RTS.
@@ -433,7 +472,7 @@
crypt->ops->extra_mpdu_prefix_len);
frag_hdr =
- (struct ieee80211_hdr_3addr *)skb_put(skb_frag, hdr_len);
+ (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
memcpy(frag_hdr, &header, hdr_len);
/* If this is not the last fragment, then add the MOREFRAGS
diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c
index b885fd1..0ea55cb 100644
--- a/net/ieee80211/ieee80211_wx.c
+++ b/net/ieee80211/ieee80211_wx.c
@@ -50,7 +50,8 @@
char *p;
struct iw_event iwe;
int i, j;
- u8 max_rate, rate;
+ char *current_val; /* For rates */
+ u8 rate;
/* First entry *MUST* be the AP MAC address */
iwe.cmd = SIOCGIWAP;
@@ -107,9 +108,13 @@
start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
/* Add basic and extended rates */
- max_rate = 0;
- p = custom;
- p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
+ /* Rate : stuffing multiple values in a single event require a bit
+ * more of magic - Jean II */
+ current_val = start + IW_EV_LCP_LEN;
+ iwe.cmd = SIOCGIWRATE;
+ /* Those two flags are ignored... */
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+
for (i = 0, j = 0; i < network->rates_len;) {
if (j < network->rates_ex_len &&
((network->rates_ex[j] & 0x7F) <
@@ -117,28 +122,21 @@
rate = network->rates_ex[j++] & 0x7F;
else
rate = network->rates[i++] & 0x7F;
- if (rate > max_rate)
- max_rate = rate;
- p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
- "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
+ /* Bit rate given in 500 kb/s units (+ 0x80) */
+ iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
+ /* Add new value to event */
+ current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN);
}
for (; j < network->rates_ex_len; j++) {
rate = network->rates_ex[j] & 0x7F;
- p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
- "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
- if (rate > max_rate)
- max_rate = rate;
+ /* Bit rate given in 500 kb/s units (+ 0x80) */
+ iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
+ /* Add new value to event */
+ current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN);
}
-
- iwe.cmd = SIOCGIWRATE;
- iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
- iwe.u.bitrate.value = max_rate * 500000;
- start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN);
-
- iwe.cmd = IWEVCUSTOM;
- iwe.u.data.length = p - custom;
- if (iwe.u.data.length)
- start = iwe_stream_add_point(start, stop, &iwe, custom);
+ /* Check if we added any rate */
+ if((current_val - start) > IW_EV_LCP_LEN)
+ start = current_val;
/* Add quality statistics */
iwe.cmd = IWEVQUAL;
diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c
index fb79ce7..ea9f5aa 100644
--- a/net/ieee80211/softmac/ieee80211softmac_assoc.c
+++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c
@@ -82,7 +82,7 @@
}
/* Sends out a disassociation request to the desired AP */
-static void
+void
ieee80211softmac_disassoc(struct ieee80211softmac_device *mac, u16 reason)
{
unsigned long flags;
diff --git a/net/ieee80211/softmac/ieee80211softmac_event.c b/net/ieee80211/softmac/ieee80211softmac_event.c
index 8cc8f3f..4b153f7 100644
--- a/net/ieee80211/softmac/ieee80211softmac_event.c
+++ b/net/ieee80211/softmac/ieee80211softmac_event.c
@@ -38,7 +38,8 @@
* The event context is private and can only be used from
* within this module. Its meaning varies with the event
* type:
- * SCAN_FINISHED: no special meaning
+ * SCAN_FINISHED,
+ * DISASSOCIATED: NULL
* ASSOCIATED,
* ASSOCIATE_FAILED,
* ASSOCIATE_TIMEOUT,
@@ -59,15 +60,15 @@
*/
static char *event_descriptions[IEEE80211SOFTMAC_EVENT_LAST+1] = {
- "scan finished",
- "associated",
+ NULL, /* scan finished */
+ NULL, /* associated */
"associating failed",
"associating timed out",
"authenticated",
"authenticating failed",
"authenticating timed out",
"associating failed because no suitable network was found",
- "disassociated",
+ NULL, /* disassociated */
};
@@ -136,30 +137,24 @@
int we_event;
char *msg = NULL;
+ memset(&wrqu, '\0', sizeof (union iwreq_data));
+
switch(event) {
case IEEE80211SOFTMAC_EVENT_ASSOCIATED:
network = (struct ieee80211softmac_network *)event_ctx;
- wrqu.data.length = 0;
- wrqu.data.flags = 0;
memcpy(wrqu.ap_addr.sa_data, &network->bssid[0], ETH_ALEN);
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- we_event = SIOCGIWAP;
- break;
+ /* fall through */
case IEEE80211SOFTMAC_EVENT_DISASSOCIATED:
- wrqu.data.length = 0;
- wrqu.data.flags = 0;
- memset(&wrqu, '\0', sizeof (union iwreq_data));
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
we_event = SIOCGIWAP;
break;
case IEEE80211SOFTMAC_EVENT_SCAN_FINISHED:
- wrqu.data.length = 0;
- wrqu.data.flags = 0;
- memset(&wrqu, '\0', sizeof (union iwreq_data));
we_event = SIOCGIWSCAN;
break;
default:
msg = event_descriptions[event];
+ if (!msg)
+ msg = "SOFTMAC EVENT BUG";
wrqu.data.length = strlen(msg);
we_event = IWEVCUSTOM;
break;
diff --git a/net/ieee80211/softmac/ieee80211softmac_priv.h b/net/ieee80211/softmac/ieee80211softmac_priv.h
index 65d9816..8c95b3a 100644
--- a/net/ieee80211/softmac/ieee80211softmac_priv.h
+++ b/net/ieee80211/softmac/ieee80211softmac_priv.h
@@ -150,6 +150,7 @@
int ieee80211softmac_handle_reassoc_req(struct net_device * dev,
struct ieee80211_reassoc_request * reassoc);
void ieee80211softmac_assoc_timeout(void *d);
+void ieee80211softmac_disassoc(struct ieee80211softmac_device *mac, u16 reason);
/* some helper functions */
static inline int ieee80211softmac_scan_handlers_check_self(struct ieee80211softmac_device *sm)
diff --git a/net/ieee80211/softmac/ieee80211softmac_wx.c b/net/ieee80211/softmac/ieee80211softmac_wx.c
index 27edb2b..8d0c226 100644
--- a/net/ieee80211/softmac/ieee80211softmac_wx.c
+++ b/net/ieee80211/softmac/ieee80211softmac_wx.c
@@ -431,3 +431,35 @@
}
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
+int
+ieee80211softmac_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ u16 reason = cpu_to_le16(mlme->reason_code);
+ struct ieee80211softmac_network *net;
+
+ if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) {
+ printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n");
+ return -EINVAL;
+ }
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data);
+ if (!net) {
+ printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n");
+ return -EINVAL;
+ }
+ return ieee80211softmac_deauth_req(mac, net, reason);
+ case IW_MLME_DISASSOC:
+ ieee80211softmac_disassoc(mac, reason);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme);