forked from luck/tmp_suning_uos_patched
Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6
This commit is contained in:
commit
388f7ef720
@ -812,7 +812,7 @@ config SMC91X
|
||||
tristate "SMC 91C9x/91C1xxx support"
|
||||
select CRC32
|
||||
select MII
|
||||
depends on NET_ETHERNET && (ARM || REDWOOD_5 || REDWOOD_6 || M32R || SUPERH)
|
||||
depends on NET_ETHERNET && (ARM || REDWOOD_5 || REDWOOD_6 || M32R || SUPERH || SOC_AU1X00)
|
||||
help
|
||||
This is a driver for SMC's 91x series of Ethernet chipsets,
|
||||
including the SMC91C94 and the SMC91C111. Say Y if you want it
|
||||
|
@ -80,7 +80,7 @@
|
||||
* into nv_close, otherwise reenabling for wol can
|
||||
* cause DMA to kfree'd memory.
|
||||
* 0.31: 14 Nov 2004: ethtool support for getting/setting link
|
||||
* capabilities.
|
||||
* capabilities.
|
||||
* 0.32: 16 Apr 2005: RX_ERROR4 handling added.
|
||||
* 0.33: 16 May 2005: Support for MCP51 added.
|
||||
* 0.34: 18 Jun 2005: Add DEV_NEED_LINKTIMER to all nForce nics.
|
||||
@ -89,14 +89,17 @@
|
||||
* 0.37: 10 Jul 2005: Additional ethtool support, cleanup of pci id list
|
||||
* 0.38: 16 Jul 2005: tx irq rewrite: Use global flags instead of
|
||||
* per-packet flags.
|
||||
* 0.39: 18 Jul 2005: Add 64bit descriptor support.
|
||||
* 0.40: 19 Jul 2005: Add support for mac address change.
|
||||
* 0.41: 30 Jul 2005: Write back original MAC in nv_close instead
|
||||
* 0.39: 18 Jul 2005: Add 64bit descriptor support.
|
||||
* 0.40: 19 Jul 2005: Add support for mac address change.
|
||||
* 0.41: 30 Jul 2005: Write back original MAC in nv_close instead
|
||||
* of nv_remove
|
||||
* 0.42: 06 Aug 2005: Fix lack of link speed initialization
|
||||
* 0.42: 06 Aug 2005: Fix lack of link speed initialization
|
||||
* in the second (and later) nv_open call
|
||||
* 0.43: 10 Aug 2005: Add support for tx checksum.
|
||||
* 0.44: 20 Aug 2005: Add support for scatter gather and segmentation.
|
||||
* 0.43: 10 Aug 2005: Add support for tx checksum.
|
||||
* 0.44: 20 Aug 2005: Add support for scatter gather and segmentation.
|
||||
* 0.45: 18 Sep 2005: Remove nv_stop/start_rx from every link check
|
||||
* 0.46: 20 Oct 2005: Add irq optimization modes.
|
||||
* 0.47: 26 Oct 2005: Add phyaddr 0 in phy scan.
|
||||
*
|
||||
* Known bugs:
|
||||
* We suspect that on some hardware no TX done interrupts are generated.
|
||||
@ -108,7 +111,7 @@
|
||||
* DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
|
||||
* superfluous timer interrupts from the nic.
|
||||
*/
|
||||
#define FORCEDETH_VERSION "0.44"
|
||||
#define FORCEDETH_VERSION "0.47"
|
||||
#define DRV_NAME "forcedeth"
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -163,7 +166,8 @@ enum {
|
||||
#define NVREG_IRQ_LINK 0x0040
|
||||
#define NVREG_IRQ_TX_ERROR 0x0080
|
||||
#define NVREG_IRQ_TX1 0x0100
|
||||
#define NVREG_IRQMASK_WANTED 0x00df
|
||||
#define NVREG_IRQMASK_THROUGHPUT 0x00df
|
||||
#define NVREG_IRQMASK_CPU 0x0040
|
||||
|
||||
#define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR| \
|
||||
NVREG_IRQ_TX_OK|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX_ERROR| \
|
||||
@ -177,7 +181,8 @@ enum {
|
||||
* NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
|
||||
*/
|
||||
NvRegPollingInterval = 0x00c,
|
||||
#define NVREG_POLL_DEFAULT 970
|
||||
#define NVREG_POLL_DEFAULT_THROUGHPUT 970
|
||||
#define NVREG_POLL_DEFAULT_CPU 13
|
||||
NvRegMisc1 = 0x080,
|
||||
#define NVREG_MISC1_HD 0x02
|
||||
#define NVREG_MISC1_FORCE 0x3b0f3c
|
||||
@ -538,6 +543,25 @@ struct fe_priv {
|
||||
*/
|
||||
static int max_interrupt_work = 5;
|
||||
|
||||
/*
|
||||
* Optimization can be either throuput mode or cpu mode
|
||||
*
|
||||
* Throughput Mode: Every tx and rx packet will generate an interrupt.
|
||||
* CPU Mode: Interrupts are controlled by a timer.
|
||||
*/
|
||||
#define NV_OPTIMIZATION_MODE_THROUGHPUT 0
|
||||
#define NV_OPTIMIZATION_MODE_CPU 1
|
||||
static int optimization_mode = NV_OPTIMIZATION_MODE_THROUGHPUT;
|
||||
|
||||
/*
|
||||
* Poll interval for timer irq
|
||||
*
|
||||
* This interval determines how frequent an interrupt is generated.
|
||||
* The is value is determined by [(time_in_micro_secs * 100) / (2^10)]
|
||||
* Min = 0, and Max = 65535
|
||||
*/
|
||||
static int poll_interval = -1;
|
||||
|
||||
static inline struct fe_priv *get_nvpriv(struct net_device *dev)
|
||||
{
|
||||
return netdev_priv(dev);
|
||||
@ -1328,67 +1352,71 @@ static void nv_rx_process(struct net_device *dev)
|
||||
if (!(Flags & NV_RX_DESCRIPTORVALID))
|
||||
goto next_pkt;
|
||||
|
||||
if (Flags & NV_RX_MISSEDFRAME) {
|
||||
np->stats.rx_missed_errors++;
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3)) {
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & NV_RX_CRCERR) {
|
||||
np->stats.rx_crc_errors++;
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & NV_RX_OVERFLOW) {
|
||||
np->stats.rx_over_errors++;
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & NV_RX_ERROR4) {
|
||||
len = nv_getlen(dev, np->rx_skbuff[i]->data, len);
|
||||
if (len < 0) {
|
||||
if (Flags & NV_RX_ERROR) {
|
||||
if (Flags & NV_RX_MISSEDFRAME) {
|
||||
np->stats.rx_missed_errors++;
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
}
|
||||
/* framing errors are soft errors. */
|
||||
if (Flags & NV_RX_FRAMINGERR) {
|
||||
if (Flags & NV_RX_SUBSTRACT1) {
|
||||
len--;
|
||||
if (Flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3)) {
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & NV_RX_CRCERR) {
|
||||
np->stats.rx_crc_errors++;
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & NV_RX_OVERFLOW) {
|
||||
np->stats.rx_over_errors++;
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & NV_RX_ERROR4) {
|
||||
len = nv_getlen(dev, np->rx_skbuff[i]->data, len);
|
||||
if (len < 0) {
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
}
|
||||
/* framing errors are soft errors. */
|
||||
if (Flags & NV_RX_FRAMINGERR) {
|
||||
if (Flags & NV_RX_SUBSTRACT1) {
|
||||
len--;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!(Flags & NV_RX2_DESCRIPTORVALID))
|
||||
goto next_pkt;
|
||||
|
||||
if (Flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3)) {
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & NV_RX2_CRCERR) {
|
||||
np->stats.rx_crc_errors++;
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & NV_RX2_OVERFLOW) {
|
||||
np->stats.rx_over_errors++;
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & NV_RX2_ERROR4) {
|
||||
len = nv_getlen(dev, np->rx_skbuff[i]->data, len);
|
||||
if (len < 0) {
|
||||
if (Flags & NV_RX2_ERROR) {
|
||||
if (Flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3)) {
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
}
|
||||
/* framing errors are soft errors */
|
||||
if (Flags & NV_RX2_FRAMINGERR) {
|
||||
if (Flags & NV_RX2_SUBSTRACT1) {
|
||||
len--;
|
||||
if (Flags & NV_RX2_CRCERR) {
|
||||
np->stats.rx_crc_errors++;
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & NV_RX2_OVERFLOW) {
|
||||
np->stats.rx_over_errors++;
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
if (Flags & NV_RX2_ERROR4) {
|
||||
len = nv_getlen(dev, np->rx_skbuff[i]->data, len);
|
||||
if (len < 0) {
|
||||
np->stats.rx_errors++;
|
||||
goto next_pkt;
|
||||
}
|
||||
}
|
||||
/* framing errors are soft errors */
|
||||
if (Flags & NV_RX2_FRAMINGERR) {
|
||||
if (Flags & NV_RX2_SUBSTRACT1) {
|
||||
len--;
|
||||
}
|
||||
}
|
||||
}
|
||||
Flags &= NV_RX2_CHECKSUMMASK;
|
||||
@ -1612,6 +1640,17 @@ static void nv_set_multicast(struct net_device *dev)
|
||||
spin_unlock_irq(&np->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* nv_update_linkspeed: Setup the MAC according to the link partner
|
||||
* @dev: Network device to be configured
|
||||
*
|
||||
* The function queries the PHY and checks if there is a link partner.
|
||||
* If yes, then it sets up the MAC accordingly. Otherwise, the MAC is
|
||||
* set to 10 MBit HD.
|
||||
*
|
||||
* The function returns 0 if there is no link partner and 1 if there is
|
||||
* a good link partner.
|
||||
*/
|
||||
static int nv_update_linkspeed(struct net_device *dev)
|
||||
{
|
||||
struct fe_priv *np = netdev_priv(dev);
|
||||
@ -1751,13 +1790,11 @@ static int nv_update_linkspeed(struct net_device *dev)
|
||||
static void nv_linkchange(struct net_device *dev)
|
||||
{
|
||||
if (nv_update_linkspeed(dev)) {
|
||||
if (netif_carrier_ok(dev)) {
|
||||
nv_stop_rx(dev);
|
||||
} else {
|
||||
if (!netif_carrier_ok(dev)) {
|
||||
netif_carrier_on(dev);
|
||||
printk(KERN_INFO "%s: link up.\n", dev->name);
|
||||
nv_start_rx(dev);
|
||||
}
|
||||
nv_start_rx(dev);
|
||||
} else {
|
||||
if (netif_carrier_ok(dev)) {
|
||||
netif_carrier_off(dev);
|
||||
@ -1799,22 +1836,18 @@ static irqreturn_t nv_nic_irq(int foo, void *data, struct pt_regs *regs)
|
||||
if (!(events & np->irqmask))
|
||||
break;
|
||||
|
||||
if (events & (NVREG_IRQ_TX1|NVREG_IRQ_TX_OK|NVREG_IRQ_TX_ERROR|NVREG_IRQ_TX_ERR)) {
|
||||
spin_lock(&np->lock);
|
||||
nv_tx_done(dev);
|
||||
spin_unlock(&np->lock);
|
||||
|
||||
nv_rx_process(dev);
|
||||
if (nv_alloc_rx(dev)) {
|
||||
spin_lock(&np->lock);
|
||||
nv_tx_done(dev);
|
||||
if (!np->in_shutdown)
|
||||
mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
|
||||
spin_unlock(&np->lock);
|
||||
}
|
||||
|
||||
if (events & (NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF)) {
|
||||
nv_rx_process(dev);
|
||||
if (nv_alloc_rx(dev)) {
|
||||
spin_lock(&np->lock);
|
||||
if (!np->in_shutdown)
|
||||
mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
|
||||
spin_unlock(&np->lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (events & NVREG_IRQ_LINK) {
|
||||
spin_lock(&np->lock);
|
||||
nv_link_irq(dev);
|
||||
@ -2216,7 +2249,14 @@ static int nv_open(struct net_device *dev)
|
||||
writel(NVREG_RNDSEED_FORCE | (i&NVREG_RNDSEED_MASK), base + NvRegRandomSeed);
|
||||
writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1);
|
||||
writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2);
|
||||
writel(NVREG_POLL_DEFAULT, base + NvRegPollingInterval);
|
||||
if (poll_interval == -1) {
|
||||
if (optimization_mode == NV_OPTIMIZATION_MODE_THROUGHPUT)
|
||||
writel(NVREG_POLL_DEFAULT_THROUGHPUT, base + NvRegPollingInterval);
|
||||
else
|
||||
writel(NVREG_POLL_DEFAULT_CPU, base + NvRegPollingInterval);
|
||||
}
|
||||
else
|
||||
writel(poll_interval & 0xFFFF, base + NvRegPollingInterval);
|
||||
writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
|
||||
writel((np->phyaddr << NVREG_ADAPTCTL_PHYSHIFT)|NVREG_ADAPTCTL_PHYVALID|NVREG_ADAPTCTL_RUNNING,
|
||||
base + NvRegAdapterControl);
|
||||
@ -2501,7 +2541,11 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
|
||||
} else {
|
||||
np->tx_flags = NV_TX2_VALID;
|
||||
}
|
||||
np->irqmask = NVREG_IRQMASK_WANTED;
|
||||
if (optimization_mode == NV_OPTIMIZATION_MODE_THROUGHPUT)
|
||||
np->irqmask = NVREG_IRQMASK_THROUGHPUT;
|
||||
else
|
||||
np->irqmask = NVREG_IRQMASK_CPU;
|
||||
|
||||
if (id->driver_data & DEV_NEED_TIMERIRQ)
|
||||
np->irqmask |= NVREG_IRQ_TIMER;
|
||||
if (id->driver_data & DEV_NEED_LINKTIMER) {
|
||||
@ -2514,16 +2558,17 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
|
||||
}
|
||||
|
||||
/* find a suitable phy */
|
||||
for (i = 1; i < 32; i++) {
|
||||
for (i = 1; i <= 32; i++) {
|
||||
int id1, id2;
|
||||
int phyaddr = i & 0x1F;
|
||||
|
||||
spin_lock_irq(&np->lock);
|
||||
id1 = mii_rw(dev, i, MII_PHYSID1, MII_READ);
|
||||
id1 = mii_rw(dev, phyaddr, MII_PHYSID1, MII_READ);
|
||||
spin_unlock_irq(&np->lock);
|
||||
if (id1 < 0 || id1 == 0xffff)
|
||||
continue;
|
||||
spin_lock_irq(&np->lock);
|
||||
id2 = mii_rw(dev, i, MII_PHYSID2, MII_READ);
|
||||
id2 = mii_rw(dev, phyaddr, MII_PHYSID2, MII_READ);
|
||||
spin_unlock_irq(&np->lock);
|
||||
if (id2 < 0 || id2 == 0xffff)
|
||||
continue;
|
||||
@ -2531,23 +2576,19 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
|
||||
id1 = (id1 & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT;
|
||||
id2 = (id2 & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT;
|
||||
dprintk(KERN_DEBUG "%s: open: Found PHY %04x:%04x at address %d.\n",
|
||||
pci_name(pci_dev), id1, id2, i);
|
||||
np->phyaddr = i;
|
||||
pci_name(pci_dev), id1, id2, phyaddr);
|
||||
np->phyaddr = phyaddr;
|
||||
np->phy_oui = id1 | id2;
|
||||
break;
|
||||
}
|
||||
if (i == 32) {
|
||||
/* PHY in isolate mode? No phy attached and user wants to
|
||||
* test loopback? Very odd, but can be correct.
|
||||
*/
|
||||
if (i == 33) {
|
||||
printk(KERN_INFO "%s: open: Could not find a valid PHY.\n",
|
||||
pci_name(pci_dev));
|
||||
}
|
||||
|
||||
if (i != 32) {
|
||||
/* reset it */
|
||||
phy_init(dev);
|
||||
pci_name(pci_dev));
|
||||
goto out_freering;
|
||||
}
|
||||
|
||||
/* reset it */
|
||||
phy_init(dev);
|
||||
|
||||
/* set default link speed settings */
|
||||
np->linkspeed = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
|
||||
@ -2689,6 +2730,10 @@ static void __exit exit_nic(void)
|
||||
|
||||
module_param(max_interrupt_work, int, 0);
|
||||
MODULE_PARM_DESC(max_interrupt_work, "forcedeth maximum events handled per interrupt");
|
||||
module_param(optimization_mode, int, 0);
|
||||
MODULE_PARM_DESC(optimization_mode, "In throughput mode (0), every tx & rx packet will generate an interrupt. In CPU mode (1), interrupts are controlled by a timer.");
|
||||
module_param(poll_interval, int, 0);
|
||||
MODULE_PARM_DESC(poll_interval, "Interval determines how frequent timer interrupt is generated by [(time_in_micro_secs * 100) / (2^10)]. Min is 0 and Max is 65535.");
|
||||
|
||||
MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>");
|
||||
MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver");
|
||||
|
@ -133,7 +133,7 @@ int gfar_mdio_probe(struct device *dev)
|
||||
if (NULL == dev)
|
||||
return -EINVAL;
|
||||
|
||||
new_bus = kmalloc(sizeof(struct mii_bus), GFP_KERNEL);
|
||||
new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
|
||||
|
||||
if (NULL == new_bus)
|
||||
return -ENOMEM;
|
||||
|
@ -72,8 +72,6 @@ static void dump_tx_desc(int dbg_lvl, struct net_device *dev, int i);
|
||||
static void dump_rx_desc(int dbg_lvl, struct net_device *dev, int i);
|
||||
static void dump_skb(int dbg_lvl, struct net_device *dev,
|
||||
struct sk_buff *skb);
|
||||
static void dump_hw_addr(int dbg_lvl, struct net_device *dev,
|
||||
const char* pfx, unsigned char* addr_str);
|
||||
static void update_stats(struct gt96100_private *gp);
|
||||
static void abort(struct net_device *dev, u32 abort_bits);
|
||||
static void hard_stop(struct net_device *dev);
|
||||
@ -334,13 +332,13 @@ dump_MII(int dbg_lvl, struct net_device *dev)
|
||||
|
||||
static void
|
||||
dump_hw_addr(int dbg_lvl, struct net_device *dev, const char* pfx,
|
||||
unsigned char* addr_str)
|
||||
const char* func, unsigned char* addr_str)
|
||||
{
|
||||
int i;
|
||||
char buf[100], octet[5];
|
||||
|
||||
if (dbg_lvl <= GT96100_DEBUG) {
|
||||
strcpy(buf, pfx);
|
||||
sprintf(buf, pfx, func);
|
||||
for (i = 0; i < 6; i++) {
|
||||
sprintf(octet, "%2.2x%s",
|
||||
addr_str[i], i<5 ? ":" : "\n");
|
||||
@ -708,7 +706,7 @@ static int __init gt96100_probe1(struct pci_dev *pci, int port_num)
|
||||
|
||||
info("%s found at 0x%x, irq %d\n",
|
||||
chip_name(gp->chip_rev), gtif->iobase, gtif->irq);
|
||||
dump_hw_addr(0, dev, "HW Address ", dev->dev_addr);
|
||||
dump_hw_addr(0, dev, "%s: HW Address ", __FUNCTION__, dev->dev_addr);
|
||||
info("%s chip revision=%d\n", chip_name(gp->chip_rev), gp->chip_rev);
|
||||
info("%s ethernet port %d\n", chip_name(gp->chip_rev), gp->port_num);
|
||||
info("external PHY ID1=0x%04x, ID2=0x%04x\n", phy_id1, phy_id2);
|
||||
@ -1488,7 +1486,7 @@ gt96100_set_rx_mode(struct net_device *dev)
|
||||
gt96100_add_hash_entry(dev, dev->dev_addr);
|
||||
|
||||
for (mcptr = dev->mc_list; mcptr; mcptr = mcptr->next) {
|
||||
dump_hw_addr(2, dev, __FUNCTION__ ": addr=",
|
||||
dump_hw_addr(2, dev, "%s: addr=", __FUNCTION__,
|
||||
mcptr->dmi_addr);
|
||||
gt96100_add_hash_entry(dev, mcptr->dmi_addr);
|
||||
}
|
||||
|
@ -58,7 +58,7 @@
|
||||
|
||||
#include "ibmveth.h"
|
||||
|
||||
#define DEBUG 1
|
||||
#undef DEBUG
|
||||
|
||||
#define ibmveth_printk(fmt, args...) \
|
||||
printk(KERN_INFO "%s: " fmt, __FILE__, ## args)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -289,6 +289,38 @@ static inline void SMC_outsw (unsigned long a, int r, unsigned char* p, int l)
|
||||
#define RPC_LSA_DEFAULT RPC_LED_TX_RX
|
||||
#define RPC_LSB_DEFAULT RPC_LED_100_10
|
||||
|
||||
#elif defined(CONFIG_SOC_AU1X00)
|
||||
|
||||
#include <au1xxx.h>
|
||||
|
||||
/* We can only do 16-bit reads and writes in the static memory space. */
|
||||
#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_inw(a, r) au_readw((unsigned long)((a) + (r)))
|
||||
#define SMC_insw(a, r, p, l) \
|
||||
do { \
|
||||
unsigned long _a = (unsigned long)((a) + (r)); \
|
||||
int _l = (l); \
|
||||
u16 *_p = (u16 *)(p); \
|
||||
while (_l-- > 0) \
|
||||
*_p++ = au_readw(_a); \
|
||||
} while(0)
|
||||
#define SMC_outw(v, a, r) au_writew(v, (unsigned long)((a) + (r)))
|
||||
#define SMC_outsw(a, r, p, l) \
|
||||
do { \
|
||||
unsigned long _a = (unsigned long)((a) + (r)); \
|
||||
int _l = (l); \
|
||||
const u16 *_p = (const u16 *)(p); \
|
||||
while (_l-- > 0) \
|
||||
au_writew(*_p++ , _a); \
|
||||
} while(0)
|
||||
|
||||
#define set_irq_type(irq, type) do {} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define SMC_CAN_USE_8BIT 1
|
||||
|
@ -330,7 +330,7 @@ config PCI_HERMES
|
||||
|
||||
config ATMEL
|
||||
tristate "Atmel at76c50x chipset 802.11b support"
|
||||
depends on NET_RADIO && EXPERIMENTAL
|
||||
depends on NET_RADIO
|
||||
select FW_LOADER
|
||||
select CRC32
|
||||
---help---
|
||||
|
@ -72,7 +72,7 @@
|
||||
#include "atmel.h"
|
||||
|
||||
#define DRIVER_MAJOR 0
|
||||
#define DRIVER_MINOR 96
|
||||
#define DRIVER_MINOR 98
|
||||
|
||||
MODULE_AUTHOR("Simon Kelley");
|
||||
MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards.");
|
||||
@ -1504,7 +1504,7 @@ static int atmel_read_proc(char *page, char **start, off_t off,
|
||||
return len;
|
||||
}
|
||||
|
||||
struct net_device *init_atmel_card( unsigned short irq, int port, const AtmelFWType fw_type,
|
||||
struct net_device *init_atmel_card( unsigned short irq, unsigned long port, const AtmelFWType fw_type,
|
||||
struct device *sys_dev, int (*card_present)(void *), void *card)
|
||||
{
|
||||
struct net_device *dev;
|
||||
@ -1605,8 +1605,8 @@ struct net_device *init_atmel_card( unsigned short irq, int port, const AtmelFWT
|
||||
goto err_out_free;
|
||||
}
|
||||
|
||||
if (priv->bus_type == BUS_TYPE_PCI &&
|
||||
!request_region( dev->base_addr, 64, dev->name )) {
|
||||
if (!request_region(dev->base_addr, 32,
|
||||
priv->bus_type == BUS_TYPE_PCCARD ? "atmel_cs" : "atmel_pci")) {
|
||||
goto err_out_irq;
|
||||
}
|
||||
|
||||
@ -1622,15 +1622,16 @@ struct net_device *init_atmel_card( unsigned short irq, int port, const AtmelFWT
|
||||
|
||||
create_proc_read_entry ("driver/atmel", 0, NULL, atmel_read_proc, priv);
|
||||
|
||||
printk(KERN_INFO "%s: Atmel at76c50x wireless. Version %d.%d simon@thekelleys.org.uk\n",
|
||||
dev->name, DRIVER_MAJOR, DRIVER_MINOR);
|
||||
printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
|
||||
dev->name, DRIVER_MAJOR, DRIVER_MINOR,
|
||||
dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
|
||||
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5] );
|
||||
|
||||
SET_MODULE_OWNER(dev);
|
||||
return dev;
|
||||
|
||||
err_out_res:
|
||||
if (priv->bus_type == BUS_TYPE_PCI)
|
||||
release_region( dev->base_addr, 64 );
|
||||
release_region( dev->base_addr, 32);
|
||||
err_out_irq:
|
||||
free_irq(dev->irq, dev);
|
||||
err_out_free:
|
||||
@ -1640,7 +1641,7 @@ struct net_device *init_atmel_card( unsigned short irq, int port, const AtmelFWT
|
||||
|
||||
EXPORT_SYMBOL(init_atmel_card);
|
||||
|
||||
void stop_atmel_card(struct net_device *dev, int freeres)
|
||||
void stop_atmel_card(struct net_device *dev)
|
||||
{
|
||||
struct atmel_private *priv = netdev_priv(dev);
|
||||
|
||||
@ -1654,10 +1655,7 @@ void stop_atmel_card(struct net_device *dev, int freeres)
|
||||
remove_proc_entry("driver/atmel", NULL);
|
||||
free_irq(dev->irq, dev);
|
||||
kfree(priv->firmware);
|
||||
if (freeres) {
|
||||
/* PCMCIA frees this stuff, so only for PCI */
|
||||
release_region(dev->base_addr, 64);
|
||||
}
|
||||
release_region(dev->base_addr, 32);
|
||||
free_netdev(dev);
|
||||
}
|
||||
|
||||
@ -1810,9 +1808,9 @@ static int atmel_set_encode(struct net_device *dev,
|
||||
}
|
||||
if(dwrq->flags & IW_ENCODE_RESTRICTED)
|
||||
priv->exclude_unencrypted = 1;
|
||||
if(dwrq->flags & IW_ENCODE_OPEN)
|
||||
if(dwrq->flags & IW_ENCODE_OPEN)
|
||||
priv->exclude_unencrypted = 0;
|
||||
|
||||
|
||||
return -EINPROGRESS; /* Call commit handler */
|
||||
}
|
||||
|
||||
@ -1827,11 +1825,12 @@ static int atmel_get_encode(struct net_device *dev,
|
||||
|
||||
if (!priv->wep_is_on)
|
||||
dwrq->flags = IW_ENCODE_DISABLED;
|
||||
else if (priv->exclude_unencrypted)
|
||||
dwrq->flags = IW_ENCODE_RESTRICTED;
|
||||
else
|
||||
dwrq->flags = IW_ENCODE_OPEN;
|
||||
|
||||
else {
|
||||
if (priv->exclude_unencrypted)
|
||||
dwrq->flags = IW_ENCODE_RESTRICTED;
|
||||
else
|
||||
dwrq->flags = IW_ENCODE_OPEN;
|
||||
}
|
||||
/* Which key do we want ? -1 -> tx index */
|
||||
if (index < 0 || index >= 4)
|
||||
index = priv->default_key;
|
||||
@ -2645,8 +2644,8 @@ static void handle_beacon_probe(struct atmel_private *priv, u16 capability, u8 c
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void send_authentication_request(struct atmel_private *priv, u8 *challenge, int challenge_len)
|
||||
|
||||
static void send_authentication_request(struct atmel_private *priv, u16 system, u8 *challenge, int challenge_len)
|
||||
{
|
||||
struct ieee80211_hdr_4addr header;
|
||||
struct auth_body auth;
|
||||
@ -2658,14 +2657,11 @@ static void send_authentication_request(struct atmel_private *priv, u8 *challeng
|
||||
memcpy(header.addr2, priv->dev->dev_addr, 6);
|
||||
memcpy(header.addr3, priv->CurrentBSSID, 6);
|
||||
|
||||
if (priv->wep_is_on) {
|
||||
auth.alg = cpu_to_le16(C80211_MGMT_AAN_SHAREDKEY);
|
||||
if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1)
|
||||
/* no WEP for authentication frames with TrSeqNo 1 */
|
||||
if (priv->CurrentAuthentTransactionSeqNum != 1)
|
||||
header.frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
||||
} else {
|
||||
auth.alg = cpu_to_le16(C80211_MGMT_AAN_OPENSYSTEM);
|
||||
}
|
||||
header.frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
||||
|
||||
auth.alg = cpu_to_le16(system);
|
||||
|
||||
auth.status = 0;
|
||||
auth.trans_seq = cpu_to_le16(priv->CurrentAuthentTransactionSeqNum);
|
||||
@ -2834,6 +2830,7 @@ static void authenticate(struct atmel_private *priv, u16 frame_len)
|
||||
struct auth_body *auth = (struct auth_body *)priv->rx_buf;
|
||||
u16 status = le16_to_cpu(auth->status);
|
||||
u16 trans_seq_no = le16_to_cpu(auth->trans_seq);
|
||||
u16 system = le16_to_cpu(auth->alg);
|
||||
|
||||
if (status == C80211_MGMT_SC_Success && !priv->wep_is_on) {
|
||||
/* no WEP */
|
||||
@ -2855,7 +2852,7 @@ static void authenticate(struct atmel_private *priv, u16 frame_len)
|
||||
|
||||
if (trans_seq_no == 0x0002 &&
|
||||
auth->el_id == C80211_MGMT_ElementID_ChallengeText) {
|
||||
send_authentication_request(priv, auth->chall_text, auth->chall_text_len);
|
||||
send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2872,14 +2869,20 @@ static void authenticate(struct atmel_private *priv, u16 frame_len)
|
||||
}
|
||||
}
|
||||
|
||||
if (status == C80211_MGMT_SC_AuthAlgNotSupported && priv->connect_to_any_BSS) {
|
||||
int bss_index;
|
||||
|
||||
priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80;
|
||||
|
||||
if ((bss_index = retrieve_bss(priv)) != -1) {
|
||||
atmel_join_bss(priv, bss_index);
|
||||
return;
|
||||
if (status == C80211_MGMT_SC_AuthAlgNotSupported) {
|
||||
/* Do opensystem first, then try sharedkey */
|
||||
if (system == C80211_MGMT_AAN_OPENSYSTEM) {
|
||||
priv->CurrentAuthentTransactionSeqNum = 0x001;
|
||||
send_authentication_request(priv, C80211_MGMT_AAN_SHAREDKEY, NULL, 0);
|
||||
} else if (priv->connect_to_any_BSS) {
|
||||
int bss_index;
|
||||
|
||||
priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80;
|
||||
|
||||
if ((bss_index = retrieve_bss(priv)) != -1) {
|
||||
atmel_join_bss(priv, bss_index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3205,7 +3208,7 @@ static void atmel_management_timer(u_long a)
|
||||
priv->AuthenticationRequestRetryCnt++;
|
||||
priv->CurrentAuthentTransactionSeqNum = 0x0001;
|
||||
mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
|
||||
send_authentication_request(priv, NULL, 0);
|
||||
send_authentication_request(priv, C80211_MGMT_AAN_OPENSYSTEM, NULL, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -3312,7 +3315,7 @@ static void atmel_command_irq(struct atmel_private *priv)
|
||||
|
||||
mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
|
||||
priv->CurrentAuthentTransactionSeqNum = 0x0001;
|
||||
send_authentication_request(priv, NULL, 0);
|
||||
send_authentication_request(priv, C80211_MGMT_AAN_SHAREDKEY, NULL, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -3482,11 +3485,6 @@ static int probe_atmel_card(struct net_device *dev)
|
||||
printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name);
|
||||
memcpy(dev->dev_addr, default_mac, 6);
|
||||
}
|
||||
printk(KERN_INFO "%s: MAC address %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
|
||||
dev->name,
|
||||
dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
|
||||
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5] );
|
||||
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
@ -35,9 +35,9 @@ typedef enum {
|
||||
ATMEL_FW_TYPE_506
|
||||
} AtmelFWType;
|
||||
|
||||
struct net_device *init_atmel_card(unsigned short, int, const AtmelFWType, struct device *,
|
||||
struct net_device *init_atmel_card(unsigned short, unsigned long, const AtmelFWType, struct device *,
|
||||
int (*present_func)(void *), void * );
|
||||
void stop_atmel_card( struct net_device *, int );
|
||||
void stop_atmel_card( struct net_device *);
|
||||
int atmel_open( struct net_device * );
|
||||
|
||||
#endif
|
||||
|
@ -63,6 +63,7 @@
|
||||
be present but disabled -- but it can then be enabled for specific
|
||||
modules at load time with a 'pc_debug=#' option to insmod.
|
||||
*/
|
||||
|
||||
#ifdef PCMCIA_DEBUG
|
||||
static int pc_debug = PCMCIA_DEBUG;
|
||||
module_param(pc_debug, int, 0);
|
||||
@ -285,41 +286,6 @@ static int card_present(void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* list of cards we know about and their firmware requirements.
|
||||
Go either by Manfid or version strings.
|
||||
Cards not in this list will need a firmware parameter to the module
|
||||
in all probability. Note that the SMC 2632 V2 and V3 have the same
|
||||
manfids, so we ignore those and use the version1 strings. */
|
||||
|
||||
static struct {
|
||||
int manf, card;
|
||||
char *ver1;
|
||||
AtmelFWType firmware;
|
||||
char *name;
|
||||
} card_table[] = {
|
||||
{ 0, 0, "WLAN/802.11b PC CARD", ATMEL_FW_TYPE_502D, "Actiontec 802CAT1" },
|
||||
{ 0, 0, "ATMEL/AT76C502AR", ATMEL_FW_TYPE_502, "NoName-RFMD" },
|
||||
{ 0, 0, "ATMEL/AT76C502AR_D", ATMEL_FW_TYPE_502D, "NoName-revD" },
|
||||
{ 0, 0, "ATMEL/AT76C502AR_E", ATMEL_FW_TYPE_502E, "NoName-revE" },
|
||||
{ 0, 0, "ATMEL/AT76C504", ATMEL_FW_TYPE_504, "NoName-504" },
|
||||
{ 0, 0, "ATMEL/AT76C504A", ATMEL_FW_TYPE_504A_2958, "NoName-504a-2958" },
|
||||
{ 0, 0, "ATMEL/AT76C504_R", ATMEL_FW_TYPE_504_2958, "NoName-504-2958" },
|
||||
{ MANFID_3COM, 0x0620, NULL, ATMEL_FW_TYPE_502_3COM, "3com 3CRWE62092B" },
|
||||
{ MANFID_3COM, 0x0696, NULL, ATMEL_FW_TYPE_502_3COM, "3com 3CRSHPW196" },
|
||||
{ 0, 0, "SMC/2632W-V2", ATMEL_FW_TYPE_502, "SMC 2632W-V2" },
|
||||
{ 0, 0, "SMC/2632W", ATMEL_FW_TYPE_502D, "SMC 2632W-V3" },
|
||||
{ 0xd601, 0x0007, NULL, ATMEL_FW_TYPE_502, "Sitecom WLAN-011" },
|
||||
{ 0x01bf, 0x3302, NULL, ATMEL_FW_TYPE_502E, "Belkin F5D6020-V2" },
|
||||
{ 0, 0, "BT/Voyager 1020 Laptop Adapter", ATMEL_FW_TYPE_502, "BT Voyager 1020" },
|
||||
{ 0, 0, "IEEE 802.11b/Wireless LAN PC Card", ATMEL_FW_TYPE_502, "Siemens Gigaset PC Card II" },
|
||||
{ 0, 0, "IEEE 802.11b/Wireless LAN Card S", ATMEL_FW_TYPE_504_2958, "Siemens Gigaset PC Card II" },
|
||||
{ 0, 0, "CNet/CNWLC 11Mbps Wireless PC Card V-5", ATMEL_FW_TYPE_502E, "CNet CNWLC-811ARL" },
|
||||
{ 0, 0, "Wireless/PC_CARD", ATMEL_FW_TYPE_502D, "Planet WL-3552" },
|
||||
{ 0, 0, "OEM/11Mbps Wireless LAN PC Card V-3", ATMEL_FW_TYPE_502, "OEM 11Mbps WLAN PCMCIA Card" },
|
||||
{ 0, 0, "11WAVE/11WP611AL-E", ATMEL_FW_TYPE_502E, "11WAVE WaveBuddy" },
|
||||
{ 0, 0, "LG/LW2100N", ATMEL_FW_TYPE_502E, "LG LW2100N 11Mbps WLAN PCMCIA Card" },
|
||||
};
|
||||
|
||||
static void atmel_config(dev_link_t *link)
|
||||
{
|
||||
client_handle_t handle;
|
||||
@ -328,10 +294,11 @@ static void atmel_config(dev_link_t *link)
|
||||
local_info_t *dev;
|
||||
int last_fn, last_ret;
|
||||
u_char buf[64];
|
||||
int card_index = -1, done = 0;
|
||||
|
||||
struct pcmcia_device_id *did;
|
||||
|
||||
handle = link->handle;
|
||||
dev = link->priv;
|
||||
did = handle_to_dev(handle).driver_data;
|
||||
|
||||
DEBUG(0, "atmel_config(0x%p)\n", link);
|
||||
|
||||
@ -340,59 +307,6 @@ static void atmel_config(dev_link_t *link)
|
||||
tuple.TupleDataMax = sizeof(buf);
|
||||
tuple.TupleOffset = 0;
|
||||
|
||||
tuple.DesiredTuple = CISTPL_MANFID;
|
||||
if (pcmcia_get_first_tuple(handle, &tuple) == 0) {
|
||||
int i;
|
||||
cistpl_manfid_t *manfid;
|
||||
CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
|
||||
CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
|
||||
manfid = &(parse.manfid);
|
||||
for (i = 0; i < sizeof(card_table)/sizeof(card_table[0]); i++) {
|
||||
if (!card_table[i].ver1 &&
|
||||
manfid->manf == card_table[i].manf &&
|
||||
manfid->card == card_table[i].card) {
|
||||
card_index = i;
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tuple.DesiredTuple = CISTPL_VERS_1;
|
||||
if (!done && (pcmcia_get_first_tuple(handle, &tuple) == 0)) {
|
||||
int i, j, k;
|
||||
cistpl_vers_1_t *ver1;
|
||||
CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
|
||||
CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
|
||||
ver1 = &(parse.version_1);
|
||||
|
||||
for (i = 0; i < sizeof(card_table)/sizeof(card_table[0]); i++) {
|
||||
for (j = 0; j < ver1->ns; j++) {
|
||||
char *p = card_table[i].ver1;
|
||||
char *q = &ver1->str[ver1->ofs[j]];
|
||||
if (!p)
|
||||
goto mismatch;
|
||||
for (k = 0; k < j; k++) {
|
||||
while ((*p != '\0') && (*p != '/')) p++;
|
||||
if (*p == '\0') {
|
||||
if (*q != '\0')
|
||||
goto mismatch;
|
||||
} else {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
while((*q != '\0') && (*p != '\0') &&
|
||||
(*p != '/') && (*p == *q)) p++, q++;
|
||||
if (((*p != '\0') && *p != '/') || *q != '\0')
|
||||
goto mismatch;
|
||||
}
|
||||
card_index = i;
|
||||
break; /* done */
|
||||
|
||||
mismatch:
|
||||
j = 0; /* dummy stmt to shut up compiler */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
This reads the card's CONFIG tuple to find its configuration
|
||||
registers.
|
||||
@ -509,12 +423,13 @@ static void atmel_config(dev_link_t *link)
|
||||
((local_info_t*)link->priv)->eth_dev =
|
||||
init_atmel_card(link->irq.AssignedIRQ,
|
||||
link->io.BasePort1,
|
||||
card_index == -1 ? ATMEL_FW_TYPE_NONE : card_table[card_index].firmware,
|
||||
did ? did->driver_info : ATMEL_FW_TYPE_NONE,
|
||||
&handle_to_dev(handle),
|
||||
card_present,
|
||||
link);
|
||||
if (!((local_info_t*)link->priv)->eth_dev)
|
||||
goto cs_failed;
|
||||
goto cs_failed;
|
||||
|
||||
|
||||
/*
|
||||
At this point, the dev_node_t structure(s) need to be
|
||||
@ -523,26 +438,7 @@ static void atmel_config(dev_link_t *link)
|
||||
strcpy(dev->node.dev_name, ((local_info_t*)link->priv)->eth_dev->name );
|
||||
dev->node.major = dev->node.minor = 0;
|
||||
link->dev = &dev->node;
|
||||
|
||||
/* Finally, report what we've done */
|
||||
printk(KERN_INFO "%s: %s%sindex 0x%02x: Vcc %d.%d",
|
||||
dev->node.dev_name,
|
||||
card_index == -1 ? "" : card_table[card_index].name,
|
||||
card_index == -1 ? "" : " ",
|
||||
link->conf.ConfigIndex,
|
||||
link->conf.Vcc/10, link->conf.Vcc%10);
|
||||
if (link->conf.Vpp1)
|
||||
printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
|
||||
if (link->conf.Attributes & CONF_ENABLE_IRQ)
|
||||
printk(", irq %d", link->irq.AssignedIRQ);
|
||||
if (link->io.NumPorts1)
|
||||
printk(", io 0x%04x-0x%04x", link->io.BasePort1,
|
||||
link->io.BasePort1+link->io.NumPorts1-1);
|
||||
if (link->io.NumPorts2)
|
||||
printk(" & 0x%04x-0x%04x", link->io.BasePort2,
|
||||
link->io.BasePort2+link->io.NumPorts2-1);
|
||||
printk("\n");
|
||||
|
||||
|
||||
link->state &= ~DEV_CONFIG_PENDING;
|
||||
return;
|
||||
|
||||
@ -569,7 +465,7 @@ static void atmel_release(dev_link_t *link)
|
||||
link->dev = NULL;
|
||||
|
||||
if (dev)
|
||||
stop_atmel_card(dev, 0);
|
||||
stop_atmel_card(dev);
|
||||
((local_info_t*)link->priv)->eth_dev = NULL;
|
||||
|
||||
/* Don't bother checking to see if these succeed or not */
|
||||
@ -637,25 +533,47 @@ static int atmel_event(event_t event, int priority,
|
||||
} /* atmel_event */
|
||||
|
||||
/*====================================================================*/
|
||||
/* We use the driver_info field to store the correct firmware type for a card. */
|
||||
|
||||
#define PCMCIA_DEVICE_MANF_CARD_INFO(manf, card, info) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
|
||||
PCMCIA_DEV_ID_MATCH_CARD_ID, \
|
||||
.manf_id = (manf), \
|
||||
.card_id = (card), \
|
||||
.driver_info = (kernel_ulong_t)(info), }
|
||||
|
||||
#define PCMCIA_DEVICE_PROD_ID12_INFO(v1, v2, vh1, vh2, info) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID2, \
|
||||
.prod_id = { (v1), (v2), NULL, NULL }, \
|
||||
.prod_id_hash = { (vh1), (vh2), 0, 0 }, \
|
||||
.driver_info = (kernel_ulong_t)(info), }
|
||||
|
||||
static struct pcmcia_device_id atmel_ids[] = {
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0620),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0696),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x3302),
|
||||
PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0007),
|
||||
PCMCIA_DEVICE_PROD_ID12("11WAVE", "11WP611AL-E", 0x9eb2da1f, 0xc9a0d3f9),
|
||||
PCMCIA_DEVICE_PROD_ID12("ATMEL", "AT76C502AR", 0xabda4164, 0x41b37e1f),
|
||||
PCMCIA_DEVICE_PROD_ID12("ATMEL", "AT76C504", 0xabda4164, 0x5040670a),
|
||||
PCMCIA_DEVICE_PROD_ID12("ATMEL", "AT76C504A", 0xabda4164, 0xe15ed87f),
|
||||
PCMCIA_DEVICE_PROD_ID12("BT", "Voyager 1020 Laptop Adapter", 0xae49b86a, 0x1e957cd5),
|
||||
PCMCIA_DEVICE_PROD_ID12("CNet", "CNWLC 11Mbps Wireless PC Card V-5", 0xbc477dde, 0x502fae6b),
|
||||
PCMCIA_DEVICE_PROD_ID12("IEEE 802.11b", "Wireless LAN PC Card", 0x5b878724, 0x122f1df6),
|
||||
PCMCIA_DEVICE_PROD_ID12("OEM", "11Mbps Wireless LAN PC Card V-3", 0xfea54c90, 0x1c5b0f68),
|
||||
PCMCIA_DEVICE_PROD_ID12("SMC", "2632W", 0xc4f8b18b, 0x30f38774),
|
||||
PCMCIA_DEVICE_PROD_ID12("SMC", "2632W-V2", 0xc4f8b18b, 0x172d1377),
|
||||
PCMCIA_DEVICE_PROD_ID12("Wireless", "PC", 0xa407ecdd, 0x556e4d7e),
|
||||
PCMCIA_DEVICE_PROD_ID12("WLAN", "802.11b PC CARD", 0x575c516c, 0xb1f6dbc4),
|
||||
PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0620, ATMEL_FW_TYPE_502_3COM),
|
||||
PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0696, ATMEL_FW_TYPE_502_3COM),
|
||||
PCMCIA_DEVICE_MANF_CARD_INFO(0x01bf, 0x3302, ATMEL_FW_TYPE_502E),
|
||||
PCMCIA_DEVICE_MANF_CARD_INFO(0xd601, 0x0007, ATMEL_FW_TYPE_502),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("11WAVE", "11WP611AL-E", 0x9eb2da1f, 0xc9a0d3f9, ATMEL_FW_TYPE_502E),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR", 0xabda4164, 0x41b37e1f, ATMEL_FW_TYPE_502),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_D", 0xabda4164, 0x3675d704, ATMEL_FW_TYPE_502D),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_E", 0xabda4164, 0x4172e792, ATMEL_FW_TYPE_502E),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504_R", 0xabda4164, 0x917f3d72, ATMEL_FW_TYPE_504_2958),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504", 0xabda4164, 0x5040670a, ATMEL_FW_TYPE_504),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504A", 0xabda4164, 0xe15ed87f, ATMEL_FW_TYPE_504A_2958),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("BT", "Voyager 1020 Laptop Adapter", 0xae49b86a, 0x1e957cd5, ATMEL_FW_TYPE_502),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("CNet", "CNWLC 11Mbps Wireless PC Card V-5", 0xbc477dde, 0x502fae6b, ATMEL_FW_TYPE_502E),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN PC Card", 0x5b878724, 0x122f1df6, ATMEL_FW_TYPE_502),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN Card S", 0x5b878724, 0x5fba533a, ATMEL_FW_TYPE_504_2958),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("OEM", "11Mbps Wireless LAN PC Card V-3", 0xfea54c90, 0x1c5b0f68, ATMEL_FW_TYPE_502),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W", 0xc4f8b18b, 0x30f38774, ATMEL_FW_TYPE_502D),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W-V2", 0xc4f8b18b, 0x172d1377, ATMEL_FW_TYPE_502),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("Wireless", "PC_CARD", 0xa407ecdd, 0x119f6314, ATMEL_FW_TYPE_502D),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("WLAN", "802.11b PC CARD", 0x575c516c, 0xb1f6dbc4, ATMEL_FW_TYPE_502D),
|
||||
PCMCIA_DEVICE_PROD_ID12_INFO("LG", "LW2100N", 0xb474d43a, 0x6b1fec94, ATMEL_FW_TYPE_502E),
|
||||
PCMCIA_DEVICE_NULL
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pcmcia, atmel_ids);
|
||||
|
||||
static struct pcmcia_driver atmel_driver = {
|
||||
|
@ -72,7 +72,7 @@ static int __devinit atmel_pci_probe(struct pci_dev *pdev,
|
||||
|
||||
static void __devexit atmel_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
stop_atmel_card(pci_get_drvdata(pdev), 1);
|
||||
stop_atmel_card(pci_get_drvdata(pdev));
|
||||
}
|
||||
|
||||
static int __init atmel_init_module(void)
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Author(s): Original Code written by
|
||||
* DJ Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
|
||||
* Rewritten by
|
||||
* Frank Pavlic (pavlic@de.ibm.com) and
|
||||
* Frank Pavlic (fpavlic@de.ibm.com) and
|
||||
* Martin Schwidefsky <schwidefsky@de.ibm.com>
|
||||
*
|
||||
* $Revision: 1.99 $ $Date: 2005/05/11 08:10:17 $
|
||||
@ -2342,6 +2342,6 @@ __exit lcs_cleanup_module(void)
|
||||
module_init(lcs_init_module);
|
||||
module_exit(lcs_cleanup_module);
|
||||
|
||||
MODULE_AUTHOR("Frank Pavlic <pavlic@de.ibm.com>");
|
||||
MODULE_AUTHOR("Frank Pavlic <fpavlic@de.ibm.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <linux/trdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/ctype.h>
|
||||
|
||||
#include <net/ipv6.h>
|
||||
#include <linux/in6.h>
|
||||
@ -24,7 +25,7 @@
|
||||
|
||||
#include "qeth_mpc.h"
|
||||
|
||||
#define VERSION_QETH_H "$Revision: 1.142 $"
|
||||
#define VERSION_QETH_H "$Revision: 1.152 $"
|
||||
|
||||
#ifdef CONFIG_QETH_IPV6
|
||||
#define QETH_VERSION_IPV6 ":IPv6"
|
||||
@ -718,8 +719,6 @@ struct qeth_reply {
|
||||
atomic_t refcnt;
|
||||
};
|
||||
|
||||
#define QETH_BROADCAST_WITH_ECHO 1
|
||||
#define QETH_BROADCAST_WITHOUT_ECHO 2
|
||||
|
||||
struct qeth_card_blkt {
|
||||
int time_total;
|
||||
@ -727,8 +726,10 @@ struct qeth_card_blkt {
|
||||
int inter_packet_jumbo;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define QETH_BROADCAST_WITH_ECHO 0x01
|
||||
#define QETH_BROADCAST_WITHOUT_ECHO 0x02
|
||||
#define QETH_LAYER2_MAC_READ 0x01
|
||||
#define QETH_LAYER2_MAC_REGISTERED 0x02
|
||||
struct qeth_card_info {
|
||||
unsigned short unit_addr2;
|
||||
unsigned short cula;
|
||||
@ -736,7 +737,7 @@ struct qeth_card_info {
|
||||
__u16 func_level;
|
||||
char mcl_level[QETH_MCL_LENGTH + 1];
|
||||
int guestlan;
|
||||
int layer2_mac_registered;
|
||||
int mac_bits;
|
||||
int portname_required;
|
||||
int portno;
|
||||
char portname[9];
|
||||
@ -749,6 +750,7 @@ struct qeth_card_info {
|
||||
int unique_id;
|
||||
struct qeth_card_blkt blkt;
|
||||
__u32 csum_mask;
|
||||
enum qeth_ipa_promisc_modes promisc_mode;
|
||||
};
|
||||
|
||||
struct qeth_card_options {
|
||||
@ -775,6 +777,7 @@ struct qeth_card_options {
|
||||
enum qeth_threads {
|
||||
QETH_SET_IP_THREAD = 1,
|
||||
QETH_RECOVER_THREAD = 2,
|
||||
QETH_SET_PROMISC_MODE_THREAD = 4,
|
||||
};
|
||||
|
||||
struct qeth_osn_info {
|
||||
@ -1074,6 +1077,26 @@ qeth_get_qdio_q_format(struct qeth_card *card)
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
qeth_isdigit(char * buf)
|
||||
{
|
||||
while (*buf) {
|
||||
if (!isdigit(*buf++))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
qeth_isxdigit(char * buf)
|
||||
{
|
||||
while (*buf) {
|
||||
if (!isxdigit(*buf++))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void
|
||||
qeth_ipaddr4_to_string(const __u8 *addr, char *buf)
|
||||
{
|
||||
@ -1090,18 +1113,27 @@ qeth_string_to_ipaddr4(const char *buf, __u8 *addr)
|
||||
int i;
|
||||
|
||||
start = buf;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (!(end = strchr(start, '.')))
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (i == 3) {
|
||||
end = strchr(start,0xa);
|
||||
if (end)
|
||||
len = end - start;
|
||||
else
|
||||
len = strlen(start);
|
||||
}
|
||||
else {
|
||||
end = strchr(start, '.');
|
||||
len = end - start;
|
||||
}
|
||||
if ((len <= 0) || (len > 3))
|
||||
return -EINVAL;
|
||||
len = end - start;
|
||||
memset(abuf, 0, 4);
|
||||
strncpy(abuf, start, len);
|
||||
if (!qeth_isdigit(abuf))
|
||||
return -EINVAL;
|
||||
addr[i] = simple_strtoul(abuf, &tmp, 10);
|
||||
start = end + 1;
|
||||
}
|
||||
memset(abuf, 0, 4);
|
||||
strcpy(abuf, start);
|
||||
addr[3] = simple_strtoul(abuf, &tmp, 10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1128,18 +1160,27 @@ qeth_string_to_ipaddr6(const char *buf, __u8 *addr)
|
||||
|
||||
tmp_addr = (u16 *)addr;
|
||||
start = buf;
|
||||
for (i = 0; i < 7; i++) {
|
||||
if (!(end = strchr(start, ':')))
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (i == 7) {
|
||||
end = strchr(start,0xa);
|
||||
if (end)
|
||||
len = end - start;
|
||||
else
|
||||
len = strlen(start);
|
||||
}
|
||||
else {
|
||||
end = strchr(start, ':');
|
||||
len = end - start;
|
||||
}
|
||||
if ((len <= 0) || (len > 4))
|
||||
return -EINVAL;
|
||||
len = end - start;
|
||||
memset(abuf, 0, 5);
|
||||
strncpy(abuf, start, len);
|
||||
if (!qeth_isxdigit(abuf))
|
||||
return -EINVAL;
|
||||
tmp_addr[i] = simple_strtoul(abuf, &tmp, 16);
|
||||
start = end + 1;
|
||||
}
|
||||
memset(abuf, 0, 5);
|
||||
strcpy(abuf, start);
|
||||
tmp_addr[7] = simple_strtoul(abuf, &tmp, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* linux/drivers/s390/net/qeth_main.c ($Revision: 1.224 $)
|
||||
* linux/drivers/s390/net/qeth_main.c ($Revision: 1.242 $)
|
||||
*
|
||||
* Linux on zSeries OSA Express and HiperSockets support
|
||||
*
|
||||
@ -9,10 +9,10 @@
|
||||
* Author(s): Original Code written by
|
||||
* Utz Bacher (utz.bacher@de.ibm.com)
|
||||
* Rewritten by
|
||||
* Frank Pavlic (pavlic@de.ibm.com) and
|
||||
* Frank Pavlic (fpavlic@de.ibm.com) and
|
||||
* Thomas Spatzier <tspat@de.ibm.com>
|
||||
*
|
||||
* $Revision: 1.224 $ $Date: 2005/05/04 20:19:18 $
|
||||
* $Revision: 1.242 $ $Date: 2005/05/04 20:19:18 $
|
||||
*
|
||||
* 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
|
||||
@ -72,7 +72,7 @@
|
||||
#include "qeth_eddp.h"
|
||||
#include "qeth_tso.h"
|
||||
|
||||
#define VERSION_QETH_C "$Revision: 1.224 $"
|
||||
#define VERSION_QETH_C "$Revision: 1.242 $"
|
||||
static const char *version = "qeth S/390 OSA-Express driver";
|
||||
|
||||
/**
|
||||
@ -159,6 +159,9 @@ qeth_get_addr_buffer(enum qeth_prot_versions);
|
||||
static void
|
||||
qeth_set_multicast_list(struct net_device *);
|
||||
|
||||
static void
|
||||
qeth_setadp_promisc_mode(struct qeth_card *);
|
||||
|
||||
static void
|
||||
qeth_notify_processes(void)
|
||||
{
|
||||
@ -602,11 +605,20 @@ __qeth_ref_ip_on_card(struct qeth_card *card, struct qeth_ipaddr *todo,
|
||||
int found = 0;
|
||||
|
||||
list_for_each_entry(addr, &card->ip_list, entry) {
|
||||
if (card->options.layer2) {
|
||||
if ((addr->type == todo->type) &&
|
||||
(memcmp(&addr->mac, &todo->mac,
|
||||
OSA_ADDR_LEN) == 0)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ((addr->proto == QETH_PROT_IPV4) &&
|
||||
(todo->proto == QETH_PROT_IPV4) &&
|
||||
(addr->type == todo->type) &&
|
||||
(addr->u.a4.addr == todo->u.a4.addr) &&
|
||||
(addr->u.a4.mask == todo->u.a4.mask) ){
|
||||
(addr->u.a4.mask == todo->u.a4.mask)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
@ -615,12 +627,12 @@ __qeth_ref_ip_on_card(struct qeth_card *card, struct qeth_ipaddr *todo,
|
||||
(addr->type == todo->type) &&
|
||||
(addr->u.a6.pfxlen == todo->u.a6.pfxlen) &&
|
||||
(memcmp(&addr->u.a6.addr, &todo->u.a6.addr,
|
||||
sizeof(struct in6_addr)) == 0)) {
|
||||
sizeof(struct in6_addr)) == 0)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found){
|
||||
if (found) {
|
||||
addr->users += todo->users;
|
||||
if (addr->users <= 0){
|
||||
*__addr = addr;
|
||||
@ -632,7 +644,7 @@ __qeth_ref_ip_on_card(struct qeth_card *card, struct qeth_ipaddr *todo,
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (todo->users > 0){
|
||||
if (todo->users > 0) {
|
||||
/* for VIPA and RXIP limit refcount to 1 */
|
||||
if (todo->type != QETH_IP_TYPE_NORMAL)
|
||||
todo->users = 1;
|
||||
@ -682,12 +694,22 @@ __qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add)
|
||||
if ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) &&
|
||||
(tmp->type == QETH_IP_TYPE_DEL_ALL_MC))
|
||||
return 0;
|
||||
if (card->options.layer2) {
|
||||
if ((tmp->type == addr->type) &&
|
||||
(tmp->is_multicast == addr->is_multicast) &&
|
||||
(memcmp(&tmp->mac, &addr->mac,
|
||||
OSA_ADDR_LEN) == 0)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ((tmp->proto == QETH_PROT_IPV4) &&
|
||||
(addr->proto == QETH_PROT_IPV4) &&
|
||||
(tmp->type == addr->type) &&
|
||||
(tmp->is_multicast == addr->is_multicast) &&
|
||||
(tmp->u.a4.addr == addr->u.a4.addr) &&
|
||||
(tmp->u.a4.mask == addr->u.a4.mask) ){
|
||||
(tmp->u.a4.mask == addr->u.a4.mask)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
@ -697,7 +719,7 @@ __qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add)
|
||||
(tmp->is_multicast == addr->is_multicast) &&
|
||||
(tmp->u.a6.pfxlen == addr->u.a6.pfxlen) &&
|
||||
(memcmp(&tmp->u.a6.addr, &addr->u.a6.addr,
|
||||
sizeof(struct in6_addr)) == 0) ){
|
||||
sizeof(struct in6_addr)) == 0)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
@ -707,7 +729,7 @@ __qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add)
|
||||
tmp->users += addr->users;
|
||||
else
|
||||
tmp->users += add? 1:-1;
|
||||
if (tmp->users == 0){
|
||||
if (tmp->users == 0) {
|
||||
list_del(&tmp->entry);
|
||||
kfree(tmp);
|
||||
}
|
||||
@ -738,12 +760,15 @@ qeth_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
|
||||
QETH_DBF_TEXT(trace,4,"delip");
|
||||
if (addr->proto == QETH_PROT_IPV4)
|
||||
QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4);
|
||||
QETH_DBF_TEXT(trace, 4, "delip");
|
||||
|
||||
if (card->options.layer2)
|
||||
QETH_DBF_HEX(trace, 4, &addr->mac, 6);
|
||||
else if (addr->proto == QETH_PROT_IPV4)
|
||||
QETH_DBF_HEX(trace, 4, &addr->u.a4.addr, 4);
|
||||
else {
|
||||
QETH_DBF_HEX(trace,4,&addr->u.a6.addr,8);
|
||||
QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+8,8);
|
||||
QETH_DBF_HEX(trace, 4, &addr->u.a6.addr, 8);
|
||||
QETH_DBF_HEX(trace, 4, ((char *)&addr->u.a6.addr) + 8, 8);
|
||||
}
|
||||
spin_lock_irqsave(&card->ip_lock, flags);
|
||||
rc = __qeth_insert_ip_todo(card, addr, 0);
|
||||
@ -757,12 +782,14 @@ qeth_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
|
||||
QETH_DBF_TEXT(trace,4,"addip");
|
||||
if (addr->proto == QETH_PROT_IPV4)
|
||||
QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4);
|
||||
QETH_DBF_TEXT(trace, 4, "addip");
|
||||
if (card->options.layer2)
|
||||
QETH_DBF_HEX(trace, 4, &addr->mac, 6);
|
||||
else if (addr->proto == QETH_PROT_IPV4)
|
||||
QETH_DBF_HEX(trace, 4, &addr->u.a4.addr, 4);
|
||||
else {
|
||||
QETH_DBF_HEX(trace,4,&addr->u.a6.addr,8);
|
||||
QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+8,8);
|
||||
QETH_DBF_HEX(trace, 4, &addr->u.a6.addr, 8);
|
||||
QETH_DBF_HEX(trace, 4, ((char *)&addr->u.a6.addr) + 8, 8);
|
||||
}
|
||||
spin_lock_irqsave(&card->ip_lock, flags);
|
||||
rc = __qeth_insert_ip_todo(card, addr, 1);
|
||||
@ -775,7 +802,7 @@ __qeth_delete_all_mc(struct qeth_card *card, unsigned long *flags)
|
||||
{
|
||||
struct qeth_ipaddr *addr, *tmp;
|
||||
int rc;
|
||||
|
||||
again:
|
||||
list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
|
||||
if (addr->is_multicast) {
|
||||
spin_unlock_irqrestore(&card->ip_lock, *flags);
|
||||
@ -784,6 +811,7 @@ __qeth_delete_all_mc(struct qeth_card *card, unsigned long *flags)
|
||||
if (!rc) {
|
||||
list_del(&addr->entry);
|
||||
kfree(addr);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -851,6 +879,7 @@ qeth_set_ip_addr_list(struct qeth_card *card)
|
||||
|
||||
static void qeth_delete_mc_addresses(struct qeth_card *);
|
||||
static void qeth_add_multicast_ipv4(struct qeth_card *);
|
||||
static void qeth_layer2_add_multicast(struct qeth_card *);
|
||||
#ifdef CONFIG_QETH_IPV6
|
||||
static void qeth_add_multicast_ipv6(struct qeth_card *);
|
||||
#endif
|
||||
@ -939,6 +968,24 @@ qeth_register_ip_addresses(void *ptr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drive the SET_PROMISC_MODE thread
|
||||
*/
|
||||
static int
|
||||
qeth_set_promisc_mode(void *ptr)
|
||||
{
|
||||
struct qeth_card *card = (struct qeth_card *) ptr;
|
||||
|
||||
daemonize("qeth_setprm");
|
||||
QETH_DBF_TEXT(trace,4,"setprm1");
|
||||
if (!qeth_do_run_thread(card, QETH_SET_PROMISC_MODE_THREAD))
|
||||
return 0;
|
||||
QETH_DBF_TEXT(trace,4,"setprm2");
|
||||
qeth_setadp_promisc_mode(card);
|
||||
qeth_clear_thread_running_bit(card, QETH_SET_PROMISC_MODE_THREAD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qeth_recover(void *ptr)
|
||||
{
|
||||
@ -1005,6 +1052,8 @@ qeth_start_kernel_thread(struct qeth_card *card)
|
||||
|
||||
if (qeth_do_start_thread(card, QETH_SET_IP_THREAD))
|
||||
kernel_thread(qeth_register_ip_addresses, (void *)card,SIGCHLD);
|
||||
if (qeth_do_start_thread(card, QETH_SET_PROMISC_MODE_THREAD))
|
||||
kernel_thread(qeth_set_promisc_mode, (void *)card, SIGCHLD);
|
||||
if (qeth_do_start_thread(card, QETH_RECOVER_THREAD))
|
||||
kernel_thread(qeth_recover, (void *) card, SIGCHLD);
|
||||
}
|
||||
@ -3749,7 +3798,7 @@ qeth_open(struct net_device *dev)
|
||||
|
||||
if ( (card->info.type != QETH_CARD_TYPE_OSN) &&
|
||||
(card->options.layer2) &&
|
||||
(!card->info.layer2_mac_registered)) {
|
||||
(!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))) {
|
||||
QETH_DBF_TEXT(trace,4,"nomacadr");
|
||||
return -EPERM;
|
||||
}
|
||||
@ -4311,6 +4360,8 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
|
||||
out:
|
||||
if (flush_count)
|
||||
qeth_flush_buffers(queue, 0, start_index, flush_count);
|
||||
else if (!atomic_read(&queue->set_pci_flags_count))
|
||||
atomic_swap(&queue->state, QETH_OUT_Q_LOCKED_FLUSH);
|
||||
/*
|
||||
* queue->state will go from LOCKED -> UNLOCKED or from
|
||||
* LOCKED_FLUSH -> LOCKED if output_handler wanted to 'notify' us
|
||||
@ -4975,6 +5026,10 @@ qeth_default_setassparms_cb(struct qeth_card *, struct qeth_reply *,
|
||||
unsigned long);
|
||||
|
||||
static int
|
||||
qeth_default_setadapterparms_cb(struct qeth_card *card,
|
||||
struct qeth_reply *reply,
|
||||
unsigned long data);
|
||||
static int
|
||||
qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *,
|
||||
__u16, long,
|
||||
int (*reply_cb)
|
||||
@ -5301,8 +5356,7 @@ qeth_free_vlan_addresses4(struct qeth_card *card, unsigned short vid)
|
||||
struct qeth_ipaddr *addr;
|
||||
|
||||
QETH_DBF_TEXT(trace, 4, "frvaddr4");
|
||||
if (!card->vlangrp)
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
in_dev = __in_dev_get_rcu(card->vlangrp->vlan_devices[vid]);
|
||||
if (!in_dev)
|
||||
@ -5330,8 +5384,7 @@ qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid)
|
||||
struct qeth_ipaddr *addr;
|
||||
|
||||
QETH_DBF_TEXT(trace, 4, "frvaddr6");
|
||||
if (!card->vlangrp)
|
||||
return;
|
||||
|
||||
in6_dev = in6_dev_get(card->vlangrp->vlan_devices[vid]);
|
||||
if (!in6_dev)
|
||||
return;
|
||||
@ -5351,10 +5404,38 @@ qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid)
|
||||
}
|
||||
|
||||
static void
|
||||
qeth_free_vlan_addresses(struct qeth_card *card, unsigned short vid)
|
||||
{
|
||||
if (card->options.layer2 || !card->vlangrp)
|
||||
return;
|
||||
qeth_free_vlan_addresses4(card, vid);
|
||||
qeth_free_vlan_addresses6(card, vid);
|
||||
}
|
||||
|
||||
static int
|
||||
qeth_layer2_send_setdelvlan_cb(struct qeth_card *card,
|
||||
struct qeth_reply *reply,
|
||||
unsigned long data)
|
||||
{
|
||||
struct qeth_ipa_cmd *cmd;
|
||||
|
||||
QETH_DBF_TEXT(trace, 2, "L2sdvcb");
|
||||
cmd = (struct qeth_ipa_cmd *) data;
|
||||
if (cmd->hdr.return_code) {
|
||||
PRINT_ERR("Error in processing VLAN %i on %s: 0x%x. "
|
||||
"Continuing\n",cmd->data.setdelvlan.vlan_id,
|
||||
QETH_CARD_IFNAME(card), cmd->hdr.return_code);
|
||||
QETH_DBF_TEXT_(trace, 2, "L2VL%4x", cmd->hdr.command);
|
||||
QETH_DBF_TEXT_(trace, 2, "L2%s", CARD_BUS_ID(card));
|
||||
QETH_DBF_TEXT_(trace, 2, "err%d", cmd->hdr.return_code);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qeth_layer2_send_setdelvlan(struct qeth_card *card, __u16 i,
|
||||
enum qeth_ipa_cmds ipacmd)
|
||||
{
|
||||
int rc;
|
||||
struct qeth_ipa_cmd *cmd;
|
||||
struct qeth_cmd_buffer *iob;
|
||||
|
||||
@ -5362,15 +5443,8 @@ qeth_layer2_send_setdelvlan(struct qeth_card *card, __u16 i,
|
||||
iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
|
||||
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
||||
cmd->data.setdelvlan.vlan_id = i;
|
||||
|
||||
rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
|
||||
if (rc) {
|
||||
PRINT_ERR("Error in processing VLAN %i on %s: 0x%x. "
|
||||
"Continuing\n",i, QETH_CARD_IFNAME(card), rc);
|
||||
QETH_DBF_TEXT_(trace, 2, "L2VL%4x", ipacmd);
|
||||
QETH_DBF_TEXT_(trace, 2, "L2%s", CARD_BUS_ID(card));
|
||||
QETH_DBF_TEXT_(trace, 2, "err%d", rc);
|
||||
}
|
||||
return qeth_send_ipa_cmd(card, iob,
|
||||
qeth_layer2_send_setdelvlan_cb, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -5420,8 +5494,7 @@ qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
|
||||
qeth_free_vlan_skbs(card, vid);
|
||||
spin_lock_irqsave(&card->vlanlock, flags);
|
||||
/* unregister IP addresses of vlan device */
|
||||
qeth_free_vlan_addresses4(card, vid);
|
||||
qeth_free_vlan_addresses6(card, vid);
|
||||
qeth_free_vlan_addresses(card, vid);
|
||||
if (card->vlangrp)
|
||||
card->vlangrp->vlan_devices[vid] = NULL;
|
||||
spin_unlock_irqrestore(&card->vlanlock, flags);
|
||||
@ -5430,6 +5503,59 @@ qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
|
||||
qeth_set_multicast_list(card->dev);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Examine hardware response to SET_PROMISC_MODE
|
||||
*/
|
||||
static int
|
||||
qeth_setadp_promisc_mode_cb(struct qeth_card *card,
|
||||
struct qeth_reply *reply,
|
||||
unsigned long data)
|
||||
{
|
||||
struct qeth_ipa_cmd *cmd;
|
||||
struct qeth_ipacmd_setadpparms *setparms;
|
||||
|
||||
QETH_DBF_TEXT(trace,4,"prmadpcb");
|
||||
|
||||
cmd = (struct qeth_ipa_cmd *) data;
|
||||
setparms = &(cmd->data.setadapterparms);
|
||||
|
||||
qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd);
|
||||
if (cmd->hdr.return_code) {
|
||||
QETH_DBF_TEXT_(trace,4,"prmrc%2.2x",cmd->hdr.return_code);
|
||||
setparms->data.mode = SET_PROMISC_MODE_OFF;
|
||||
}
|
||||
card->info.promisc_mode = setparms->data.mode;
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Set promiscuous mode (on or off) (SET_PROMISC_MODE command)
|
||||
*/
|
||||
static void
|
||||
qeth_setadp_promisc_mode(struct qeth_card *card)
|
||||
{
|
||||
enum qeth_ipa_promisc_modes mode;
|
||||
struct net_device *dev = card->dev;
|
||||
struct qeth_cmd_buffer *iob;
|
||||
struct qeth_ipa_cmd *cmd;
|
||||
|
||||
QETH_DBF_TEXT(trace, 4, "setprom");
|
||||
|
||||
if (((dev->flags & IFF_PROMISC) &&
|
||||
(card->info.promisc_mode == SET_PROMISC_MODE_ON)) ||
|
||||
(!(dev->flags & IFF_PROMISC) &&
|
||||
(card->info.promisc_mode == SET_PROMISC_MODE_OFF)))
|
||||
return;
|
||||
mode = SET_PROMISC_MODE_OFF;
|
||||
if (dev->flags & IFF_PROMISC)
|
||||
mode = SET_PROMISC_MODE_ON;
|
||||
QETH_DBF_TEXT_(trace, 4, "mode:%x", mode);
|
||||
|
||||
iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_PROMISC_MODE,
|
||||
sizeof(struct qeth_ipacmd_setadpparms));
|
||||
cmd = (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE);
|
||||
cmd->data.setadapterparms.data.mode = mode;
|
||||
qeth_send_ipa_cmd(card, iob, qeth_setadp_promisc_mode_cb, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* set multicast address on card
|
||||
@ -5444,12 +5570,22 @@ qeth_set_multicast_list(struct net_device *dev)
|
||||
|
||||
QETH_DBF_TEXT(trace,3,"setmulti");
|
||||
qeth_delete_mc_addresses(card);
|
||||
if (card->options.layer2) {
|
||||
qeth_layer2_add_multicast(card);
|
||||
goto out;
|
||||
}
|
||||
qeth_add_multicast_ipv4(card);
|
||||
#ifdef CONFIG_QETH_IPV6
|
||||
qeth_add_multicast_ipv6(card);
|
||||
#endif
|
||||
out:
|
||||
if (qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD) == 0)
|
||||
schedule_work(&card->kernel_thread_starter);
|
||||
if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
|
||||
return;
|
||||
if (qeth_set_thread_start_bit(card, QETH_SET_PROMISC_MODE_THREAD)==0)
|
||||
schedule_work(&card->kernel_thread_starter);
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
@ -5657,6 +5793,24 @@ qeth_add_multicast_ipv4(struct qeth_card *card)
|
||||
in_dev_put(in4_dev);
|
||||
}
|
||||
|
||||
static void
|
||||
qeth_layer2_add_multicast(struct qeth_card *card)
|
||||
{
|
||||
struct qeth_ipaddr *ipm;
|
||||
struct dev_mc_list *dm;
|
||||
|
||||
QETH_DBF_TEXT(trace,4,"L2addmc");
|
||||
for (dm = card->dev->mc_list; dm; dm = dm->next) {
|
||||
ipm = qeth_get_addr_buffer(QETH_PROT_IPV4);
|
||||
if (!ipm)
|
||||
continue;
|
||||
memcpy(ipm->mac,dm->dmi_addr,MAX_ADDR_LEN);
|
||||
ipm->is_multicast = 1;
|
||||
if (!qeth_add_ip(card, ipm))
|
||||
kfree(ipm);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QETH_IPV6
|
||||
static inline void
|
||||
qeth_add_mc6(struct qeth_card *card, struct inet6_dev *in6_dev)
|
||||
@ -5825,10 +5979,10 @@ qeth_layer2_send_setmac_cb(struct qeth_card *card,
|
||||
PRINT_WARN("Error in registering MAC address on " \
|
||||
"device %s: x%x\n", CARD_BUS_ID(card),
|
||||
cmd->hdr.return_code);
|
||||
card->info.layer2_mac_registered = 0;
|
||||
card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
|
||||
cmd->hdr.return_code = -EIO;
|
||||
} else {
|
||||
card->info.layer2_mac_registered = 1;
|
||||
card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
|
||||
memcpy(card->dev->dev_addr,cmd->data.setdelmac.mac,
|
||||
OSA_ADDR_LEN);
|
||||
PRINT_INFO("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
|
||||
@ -5866,7 +6020,7 @@ qeth_layer2_send_delmac_cb(struct qeth_card *card,
|
||||
cmd->hdr.return_code = -EIO;
|
||||
return 0;
|
||||
}
|
||||
card->info.layer2_mac_registered = 0;
|
||||
card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -5874,7 +6028,7 @@ static int
|
||||
qeth_layer2_send_delmac(struct qeth_card *card, __u8 *mac)
|
||||
{
|
||||
QETH_DBF_TEXT(trace, 2, "L2Delmac");
|
||||
if (!card->info.layer2_mac_registered)
|
||||
if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))
|
||||
return 0;
|
||||
return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_DELVMAC,
|
||||
qeth_layer2_send_delmac_cb);
|
||||
@ -5896,7 +6050,7 @@ qeth_layer2_set_mac_address(struct net_device *dev, void *p)
|
||||
card = (struct qeth_card *) dev->priv;
|
||||
|
||||
if (!card->options.layer2) {
|
||||
PRINT_WARN("Setting MAC address on %s is not supported"
|
||||
PRINT_WARN("Setting MAC address on %s is not supported "
|
||||
"in Layer 3 mode.\n", dev->name);
|
||||
QETH_DBF_TEXT(trace, 3, "setmcLY3");
|
||||
return -EOPNOTSUPP;
|
||||
@ -6441,6 +6595,8 @@ qeth_default_setadapterparms_cb(struct qeth_card *card,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
qeth_query_setadapterparms_cb(struct qeth_card *card, struct qeth_reply *reply,
|
||||
unsigned long data)
|
||||
@ -6481,8 +6637,13 @@ qeth_setadpparms_change_macaddr_cb(struct qeth_card *card,
|
||||
QETH_DBF_TEXT(trace,4,"chgmaccb");
|
||||
|
||||
cmd = (struct qeth_ipa_cmd *) data;
|
||||
memcpy(card->dev->dev_addr,
|
||||
&cmd->data.setadapterparms.data.change_addr.addr,OSA_ADDR_LEN);
|
||||
if (!card->options.layer2 || card->info.guestlan ||
|
||||
!(card->info.mac_bits & QETH_LAYER2_MAC_READ)) {
|
||||
memcpy(card->dev->dev_addr,
|
||||
&cmd->data.setadapterparms.data.change_addr.addr,
|
||||
OSA_ADDR_LEN);
|
||||
card->info.mac_bits |= QETH_LAYER2_MAC_READ;
|
||||
}
|
||||
qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
|
||||
return 0;
|
||||
}
|
||||
@ -6602,6 +6763,12 @@ qeth_layer2_initialize(struct qeth_card *card)
|
||||
QETH_DBF_TEXT(setup, 2, "doL2init");
|
||||
QETH_DBF_TEXT_(setup, 2, "doL2%s", CARD_BUS_ID(card));
|
||||
|
||||
rc = qeth_query_setadapterparms(card);
|
||||
if (rc) {
|
||||
PRINT_WARN("could not query adapter parameters on device %s: "
|
||||
"x%x\n", CARD_BUS_ID(card), rc);
|
||||
}
|
||||
|
||||
rc = qeth_setadpparms_change_macaddr(card);
|
||||
if (rc) {
|
||||
PRINT_WARN("couldn't get MAC address on "
|
||||
@ -8548,7 +8715,7 @@ EXPORT_SYMBOL(qeth_osn_deregister);
|
||||
EXPORT_SYMBOL(qeth_osn_assist);
|
||||
module_init(qeth_init);
|
||||
module_exit(qeth_exit);
|
||||
MODULE_AUTHOR("Frank Pavlic <pavlic@de.ibm.com>");
|
||||
MODULE_AUTHOR("Frank Pavlic <fpavlic@de.ibm.com>");
|
||||
MODULE_DESCRIPTION("Linux on zSeries OSA Express and HiperSockets support\n" \
|
||||
"Copyright 2000,2003 IBM Corporation\n");
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Linux on zSeries OSA Express and HiperSockets support
|
||||
*
|
||||
* Copyright 2000,2003 IBM Corporation
|
||||
* Author(s): Frank Pavlic <pavlic@de.ibm.com>
|
||||
* Author(s): Frank Pavlic <fpavlic@de.ibm.com>
|
||||
* Thomas Spatzier <tspat@de.ibm.com>
|
||||
*
|
||||
*/
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Copyright 2000,2003 IBM Corporation
|
||||
* Author(s): Utz Bacher <utz.bacher@de.ibm.com>
|
||||
* Thomas Spatzier <tspat@de.ibm.com>
|
||||
* Frank Pavlic <pavlic@de.ibm.com>
|
||||
* Frank Pavlic <fpavlic@de.ibm.com>
|
||||
*
|
||||
*/
|
||||
#ifndef __QETH_MPC_H__
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
#include <asm/qeth.h>
|
||||
|
||||
#define VERSION_QETH_MPC_H "$Revision: 1.43 $"
|
||||
#define VERSION_QETH_MPC_H "$Revision: 1.44 $"
|
||||
|
||||
extern const char *VERSION_QETH_MPC_C;
|
||||
|
||||
@ -217,7 +217,7 @@ enum qeth_ipa_setadp_cmd {
|
||||
IPA_SETADP_SEND_OSA_MESSAGE = 0x0100,
|
||||
IPA_SETADP_SET_SNMP_CONTROL = 0x0200,
|
||||
IPA_SETADP_READ_SNMP_PARMS = 0x0400,
|
||||
IPA_SETADP_WRITE_SNMP_PARMS = 0x0800,
|
||||
IPA_SETADP_SET_PROMISC_MODE = 0x0800,
|
||||
IPA_SETADP_QUERY_CARD_INFO = 0x1000,
|
||||
};
|
||||
enum qeth_ipa_mac_ops {
|
||||
@ -232,9 +232,12 @@ enum qeth_ipa_addr_ops {
|
||||
CHANGE_ADDR_ADD_ADDR = 1,
|
||||
CHANGE_ADDR_DEL_ADDR = 2,
|
||||
CHANGE_ADDR_FLUSH_ADDR_TABLE = 4,
|
||||
|
||||
|
||||
};
|
||||
enum qeth_ipa_promisc_modes {
|
||||
SET_PROMISC_MODE_OFF = 0,
|
||||
SET_PROMISC_MODE_ON = 1,
|
||||
};
|
||||
|
||||
/* (SET)DELIP(M) IPA stuff ***************************************************/
|
||||
struct qeth_ipacmd_setdelip4 {
|
||||
__u8 ip_addr[4];
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* linux/drivers/s390/net/qeth_sys.c ($Revision: 1.55 $)
|
||||
* linux/drivers/s390/net/qeth_sys.c ($Revision: 1.58 $)
|
||||
*
|
||||
* Linux on zSeries OSA Express and HiperSockets support
|
||||
* This file contains code related to sysfs.
|
||||
@ -8,7 +8,7 @@
|
||||
* Copyright 2000,2003 IBM Corporation
|
||||
*
|
||||
* Author(s): Thomas Spatzier <tspat@de.ibm.com>
|
||||
* Frank Pavlic <pavlic@de.ibm.com>
|
||||
* Frank Pavlic <fpavlic@de.ibm.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/list.h>
|
||||
@ -20,7 +20,7 @@
|
||||
#include "qeth_mpc.h"
|
||||
#include "qeth_fs.h"
|
||||
|
||||
const char *VERSION_QETH_SYS_C = "$Revision: 1.55 $";
|
||||
const char *VERSION_QETH_SYS_C = "$Revision: 1.58 $";
|
||||
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
@ -1117,7 +1117,7 @@ qeth_parse_ipatoe(const char* buf, enum qeth_prot_versions proto,
|
||||
start = buf;
|
||||
/* get address string */
|
||||
end = strchr(start, '/');
|
||||
if (!end){
|
||||
if (!end || (end-start >= 49)){
|
||||
PRINT_WARN("Invalid format for ipato_addx/delx. "
|
||||
"Use <ip addr>/<mask bits>\n");
|
||||
return -EINVAL;
|
||||
|
@ -5,7 +5,7 @@
|
||||
*
|
||||
* Copyright 2004 IBM Corporation
|
||||
*
|
||||
* Author(s): Frank Pavlic <pavlic@de.ibm.com>
|
||||
* Author(s): Frank Pavlic <fpavlic@de.ibm.com>
|
||||
*
|
||||
* $Revision: 1.7 $ $Date: 2005/05/04 20:19:18 $
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user