diff options
39 files changed, 6688 insertions, 873 deletions
diff --git a/Documentation/networking/LICENSE.qla3xxx b/Documentation/networking/LICENSE.qla3xxx new file mode 100644 index 00000000000..2f2077e34d8 --- /dev/null +++ b/Documentation/networking/LICENSE.qla3xxx @@ -0,0 +1,46 @@ +Copyright (c) 2003-2006 QLogic Corporation +QLogic Linux Networking HBA Driver + +This program includes a device driver for Linux 2.6 that may be +distributed with QLogic hardware specific firmware binary file. +You may modify and redistribute the device driver code under the +GNU General Public License as published by the Free Software +Foundation (version 2 or a later version). + +You may redistribute the hardware specific firmware binary file +under the following terms: + + 1. Redistribution of source code (only if applicable), + must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistribution in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + + 3. The name of QLogic Corporation may not be used to + endorse or promote products derived from this software + without specific prior written permission + +REGARDLESS OF WHAT LICENSING MECHANISM IS USED OR APPLICABLE, +THIS PROGRAM IS PROVIDED BY QLOGIC CORPORATION "AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +USER ACKNOWLEDGES AND AGREES THAT USE OF THIS PROGRAM WILL NOT +CREATE OR GIVE GROUNDS FOR A LICENSE BY IMPLICATION, ESTOPPEL, OR +OTHERWISE IN ANY INTELLECTUAL PROPERTY RIGHTS (PATENT, COPYRIGHT, +TRADE SECRET, MASK WORK, OR OTHER PROPRIETARY RIGHT) EMBODIED IN +ANY OTHER QLOGIC HARDWARE OR SOFTWARE EITHER SOLELY OR IN +COMBINATION WITH THIS PROGRAM. + diff --git a/MAINTAINERS b/MAINTAINERS index e3e1515ba5a..13f345cf77c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2360,6 +2360,12 @@ M: linux-driver@qlogic.com L: linux-scsi@vger.kernel.org S: Supported +QLOGIC QLA3XXX NETWORK DRIVER +P: Ron Mercer +M: linux-driver@qlogic.com +L: netdev@vger.kernel.org +S: Supported + QNX4 FILESYSTEM P: Anders Larsen M: al@alarsen.net @@ -2610,6 +2616,18 @@ P: Nicolas Pitre M: nico@cam.org S: Maintained +SOFTMAC LAYER (IEEE 802.11) +P: Johannes Berg +M: johannes@sipsolutions.net +P: Joe Jezak +M: josejx@gentoo.org +P: Daniel Drake +M: dsd@gentoo.org +W: http://softmac.sipsolutions.net/ +L: softmac-dev@sipsolutions.net +L: netdev@vger.kernel.org +S: Maintained + SOFTWARE RAID (Multiple Disks) SUPPORT P: Ingo Molnar M: mingo@redhat.com @@ -3326,6 +3344,15 @@ W: http://www.qsl.net/dl1bke/ L: linux-hams@vger.kernel.org S: Maintained +ZD1211RW WIRELESS DRIVER +P: Daniel Drake +M: dsd@gentoo.org +P: Ulrich Kunitz +M: kune@deine-taler.de +W: http://zd1211.ath.cx/wiki/DriverRewrite +L: zd1211-devs@lists.sourceforge.net (subscribers-only) +S: Maintained + ZF MACHZ WATCHDOG P: Fernando Fuganti M: fuganti@netbank.com.br diff --git a/drivers/isdn/i4l/Kconfig b/drivers/isdn/i4l/Kconfig index a4f7288a1fc..3ef567b99c7 100644 --- a/drivers/isdn/i4l/Kconfig +++ b/drivers/isdn/i4l/Kconfig @@ -5,6 +5,7 @@ config ISDN_PPP bool "Support synchronous PPP" depends on INET + select SLHC help Over digital connections such as ISDN, there is no need to synchronize sender and recipient's clocks with start and stop bits diff --git a/drivers/net/8390.c b/drivers/net/8390.c index d2935ae3981..3eb7048684a 100644 --- a/drivers/net/8390.c +++ b/drivers/net/8390.c @@ -299,7 +299,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) * Slow phase with lock held. */ - disable_irq_nosync(dev->irq); + disable_irq_nosync_lockdep(dev->irq); spin_lock(&ei_local->page_lock); @@ -338,7 +338,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); outb_p(ENISR_ALL, e8390_base + EN0_IMR); spin_unlock(&ei_local->page_lock); - enable_irq(dev->irq); + enable_irq_lockdep(dev->irq); ei_local->stat.tx_errors++; return 1; } @@ -379,7 +379,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) outb_p(ENISR_ALL, e8390_base + EN0_IMR); spin_unlock(&ei_local->page_lock); - enable_irq(dev->irq); + enable_irq_lockdep(dev->irq); dev_kfree_skb (skb); ei_local->stat.tx_bytes += send_length; @@ -505,9 +505,9 @@ irqreturn_t ei_interrupt(int irq, void *dev_id, struct pt_regs * regs) #ifdef CONFIG_NET_POLL_CONTROLLER void ei_poll(struct net_device *dev) { - disable_irq(dev->irq); + disable_irq_lockdep(dev->irq); ei_interrupt(dev->irq, dev, NULL); - enable_irq(dev->irq); + enable_irq_lockdep(dev->irq); } #endif diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 39189903e35..ecfbd1c2408 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1411,6 +1411,22 @@ config FORCEDETH <file:Documentation/networking/net-modules.txt>. The module will be called forcedeth. +config FORCEDETH_NAPI + bool "Use Rx and Tx Polling (NAPI) (EXPERIMENTAL)" + depends on FORCEDETH && EXPERIMENTAL + help + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + + See <file:Documentation/networking/NAPI_HOWTO.txt> for more + information. + + If in doubt, say N. config CS89x0 tristate "CS89x0 support" @@ -2249,6 +2265,15 @@ config MV643XX_ETH_2 This enables support for Port 2 of the Marvell MV643XX Gigabit Ethernet. +config QLA3XXX + tristate "QLogic QLA3XXX Network Driver Support" + depends on PCI + help + This driver supports QLogic ISP3XXX gigabit Ethernet cards. + + To compile this driver as a module, choose M here: the module + will be called qla3xxx. + endmenu # @@ -2509,6 +2534,7 @@ config PLIP config PPP tristate "PPP (point-to-point protocol) support" + select SLHC ---help--- PPP (Point to Point Protocol) is a newer and better SLIP. It serves the same purpose: sending Internet traffic over telephone (and other @@ -2689,6 +2715,7 @@ config SLIP config SLIP_COMPRESSED bool "CSLIP compressed headers" depends on SLIP + select SLHC ---help--- This protocol is faster than SLIP because it uses compression on the TCP/IP headers (not on the data itself), but it has to be supported @@ -2701,6 +2728,12 @@ config SLIP_COMPRESSED <http://www.tldp.org/docs.html#howto>, explains how to configure CSLIP. This won't enlarge your kernel. +config SLHC + tristate + help + This option enables Van Jacobsen serial line header compression + routines. + config SLIP_SMART bool "Keepalive and linefill" depends on SLIP diff --git a/drivers/net/Makefile b/drivers/net/Makefile index c91e95126f7..5e91c3562ad 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -2,10 +2,6 @@ # Makefile for the Linux network (ethercard) device drivers. # -ifeq ($(CONFIG_ISDN_PPP),y) - obj-$(CONFIG_ISDN) += slhc.o -endif - obj-$(CONFIG_E1000) += e1000/ obj-$(CONFIG_IBM_EMAC) += ibm_emac/ obj-$(CONFIG_IXGB) += ixgb/ @@ -110,8 +106,9 @@ obj-$(CONFIG_FORCEDETH) += forcedeth.o obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o +obj-$(CONFIG_QLA3XXX) += qla3xxx.o -obj-$(CONFIG_PPP) += ppp_generic.o slhc.o +obj-$(CONFIG_PPP) += ppp_generic.o obj-$(CONFIG_PPP_ASYNC) += ppp_async.o obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o obj-$(CONFIG_PPP_DEFLATE) += ppp_deflate.o @@ -120,9 +117,7 @@ obj-$(CONFIG_PPP_MPPE) += ppp_mppe.o obj-$(CONFIG_PPPOE) += pppox.o pppoe.o obj-$(CONFIG_SLIP) += slip.o -ifeq ($(CONFIG_SLIP_COMPRESSED),y) - obj-$(CONFIG_SLIP) += slhc.o -endif +obj-$(CONFIG_SLHC) += slhc.o obj-$(CONFIG_DUMMY) += dummy.o obj-$(CONFIG_IFB) += ifb.o diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 11b8f1b43dd..a2aca92e8b2 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -109,6 +109,7 @@ * 0.54: 21 Mar 2006: Fix spin locks for multi irqs and cleanup. * 0.55: 22 Mar 2006: Add flow control (pause frame). * 0.56: 22 Mar 2006: Additional ethtool config and moduleparam support. + * 0.57: 14 May 2006: Mac address set in probe/remove and order corrections. * * Known bugs: * We suspect that on some hardware no TX done interrupts are generated. @@ -120,7 +121,12 @@ * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few * superfluous timer interrupts from the nic. */ -#define FORCEDETH_VERSION "0.56" +#ifdef CONFIG_FORCEDETH_NAPI +#define DRIVERNAPI "-NAPI" +#else +#define DRIVERNAPI +#endif +#define FORCEDETH_VERSION "0.57" #define DRV_NAME "forcedeth" #include <linux/module.h> @@ -262,7 +268,8 @@ enum { NvRegRingSizes = 0x108, #define NVREG_RINGSZ_TXSHIFT 0 #define NVREG_RINGSZ_RXSHIFT 16 - NvRegUnknownTransmitterReg = 0x10c, + NvRegTransmitPoll = 0x10c, +#define NVREG_TRANSMITPOLL_MAC_ADDR_REV 0x00008000 NvRegLinkSpeed = 0x110, #define NVREG_LINKSPEED_FORCE 0x10000 #define NVREG_LINKSPEED_10 1000 @@ -381,21 +388,21 @@ enum { /* Big endian: should work, but is untested */ struct ring_desc { - u32 PacketBuffer; - u32 FlagLen; + __le32 buf; + __le32 flaglen; }; struct ring_desc_ex { - u32 PacketBufferHigh; - u32 PacketBufferLow; - u32 TxVlan; - u32 FlagLen; + __le32 bufhigh; + __le32 buflow; + __le32 txvlan; + __le32 flaglen; }; -typedef union _ring_type { +union ring_type { struct ring_desc* orig; struct ring_desc_ex* ex; -} ring_type; +}; #define FLAG_MASK_V1 0xffff0000 #define FLAG_MASK_V2 0xffffc000 @@ -653,8 +660,8 @@ static const struct nv_ethtool_str nv_etests_str[] = { }; struct register_test { - u32 reg; - u32 mask; + __le32 reg; + __le32 mask; }; static const struct register_test nv_registers_test[] = { @@ -713,7 +720,7 @@ struct fe_priv { /* rx specific fields. * Locking: Within irq hander or disable_irq+spin_lock(&np->lock); */ - ring_type rx_ring; + union ring_type rx_ring; unsigned int cur_rx, refill_rx; struct sk_buff **rx_skbuff; dma_addr_t *rx_dma; @@ -733,7 +740,7 @@ struct fe_priv { /* * tx specific fields. */ - ring_type tx_ring; + union ring_type tx_ring; unsigned int next_tx, nic_tx; struct sk_buff **tx_skbuff; dma_addr_t *tx_dma; @@ -826,13 +833,13 @@ static inline void pci_push(u8 __iomem *base) static inline u32 nv_descr_getlength(struct ring_desc *prd, u32 v) { - return le32_to_cpu(prd->FlagLen) + return le32_to_cpu(prd->flaglen) & ((v == DESC_VER_1) ? LEN_MASK_V1 : LEN_MASK_V2); } static inline u32 nv_descr_getlength_ex(struct ring_desc_ex *prd, u32 v) { - return le32_to_cpu(prd->FlagLen) & LEN_MASK_V2; + return le32_to_cpu(prd->flaglen) & LEN_MASK_V2; } static int reg_delay(struct net_device *dev, int offset, u32 mask, u32 target, @@ -885,7 +892,7 @@ static void free_rings(struct net_device *dev) struct fe_priv *np = get_nvpriv(dev); if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - if(np->rx_ring.orig) + if (np->rx_ring.orig) pci_free_consistent(np->pci_dev, sizeof(struct ring_desc) * (np->rx_ring_size + np->tx_ring_size), np->rx_ring.orig, np->ring_addr); } else { @@ -1178,7 +1185,7 @@ static void nv_stop_tx(struct net_device *dev) KERN_INFO "nv_stop_tx: TransmitterStatus remained busy"); udelay(NV_TXSTOP_DELAY2); - writel(0, base + NvRegUnknownTransmitterReg); + writel(readl(base + NvRegTransmitPoll) & NVREG_TRANSMITPOLL_MAC_ADDR_REV, base + NvRegTransmitPoll); } static void nv_txrx_reset(struct net_device *dev) @@ -1258,14 +1265,14 @@ static int nv_alloc_rx(struct net_device *dev) np->rx_dma[nr] = pci_map_single(np->pci_dev, skb->data, skb->end-skb->data, PCI_DMA_FROMDEVICE); if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - np->rx_ring.orig[nr].PacketBuffer = cpu_to_le32(np->rx_dma[nr]); + np->rx_ring.orig[nr].buf = cpu_to_le32(np->rx_dma[nr]); wmb(); - np->rx_ring.orig[nr].FlagLen = cpu_to_le32(np->rx_buf_sz | NV_RX_AVAIL); + np->rx_ring.orig[nr].flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX_AVAIL); } else { - np->rx_ring.ex[nr].PacketBufferHigh = cpu_to_le64(np->rx_dma[nr]) >> 32; - np->rx_ring.ex[nr].PacketBufferLow = cpu_to_le64(np->rx_dma[nr]) & 0x0FFFFFFFF; + np->rx_ring.ex[nr].bufhigh = cpu_to_le64(np->rx_dma[nr]) >> 32; + np->rx_ring.ex[nr].buflow = cpu_to_le64(np->rx_dma[nr]) & 0x0FFFFFFFF; wmb(); - np->rx_ring.ex[nr].FlagLen = cpu_to_le32(np->rx_buf_sz | NV_RX2_AVAIL); + np->rx_ring.ex[nr].flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX2_AVAIL); } dprintk(KERN_DEBUG "%s: nv_alloc_rx: Packet %d marked as Available\n", dev->name, refill_rx); @@ -1277,6 +1284,16 @@ static int nv_alloc_rx(struct net_device *dev) return 0; } +/* If rx bufs are exhausted called after 50ms to attempt to refresh */ +#ifdef CONFIG_FORCEDETH_NAPI +static void nv_do_rx_refill(unsigned long data) +{ + struct net_device *dev = (struct net_device *) data; + + /* Just reschedule NAPI rx processing */ + netif_rx_schedule(dev); +} +#else static void nv_do_rx_refill(unsigned long data) { struct net_device *dev = (struct net_device *) data; @@ -1305,6 +1322,7 @@ static void nv_do_rx_refill(unsigned long data) enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector); } } +#endif static void nv_init_rx(struct net_device *dev) { @@ -1315,9 +1333,9 @@ static void nv_init_rx(struct net_device *dev) np->refill_rx = 0; for (i = 0; i < np->rx_ring_size; i++) if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) - np->rx_ring.orig[i].FlagLen = 0; + np->rx_ring.orig[i].flaglen = 0; else - np->rx_ring.ex[i].FlagLen = 0; + np->rx_ring.ex[i].flaglen = 0; } static void nv_init_tx(struct net_device *dev) @@ -1328,9 +1346,9 @@ static void nv_init_tx(struct net_device *dev) np->next_tx = np->nic_tx = 0; for (i = 0; i < np->tx_ring_size; i++) { if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) - np->tx_ring.orig[i].FlagLen = 0; + np->tx_ring.orig[i].flaglen = 0; else - np->tx_ring.ex[i].FlagLen = 0; + np->tx_ring.ex[i].flaglen = 0; np->tx_skbuff[i] = NULL; np->tx_dma[i] = 0; } @@ -1373,9 +1391,9 @@ static void nv_drain_tx(struct net_device *dev) for (i = 0; i < np->tx_ring_size; i++) { if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) - np->tx_ring.orig[i].FlagLen = 0; + np->tx_ring.orig[i].flaglen = 0; else - np->tx_ring.ex[i].FlagLen = 0; + np->tx_ring.ex[i].flaglen = 0; if (nv_release_txskb(dev, i)) np->stats.tx_dropped++; } @@ -1387,9 +1405,9 @@ static void nv_drain_rx(struct net_device *dev) int i; for (i = 0; i < np->rx_ring_size; i++) { if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) - np->rx_ring.orig[i].FlagLen = 0; + np->rx_ring.orig[i].flaglen = 0; else - np->rx_ring.ex[i].FlagLen = 0; + np->rx_ring.ex[i].flaglen = 0; wmb(); if (np->rx_skbuff[i]) { pci_unmap_single(np->pci_dev, np->rx_dma[i], @@ -1450,17 +1468,17 @@ static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev) np->tx_dma_len[nr] = bcnt; if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - np->tx_ring.orig[nr].PacketBuffer = cpu_to_le32(np->tx_dma[nr]); - np->tx_ring.orig[nr].FlagLen = cpu_to_le32((bcnt-1) | tx_flags); + np->tx_ring.orig[nr].buf = cpu_to_le32(np->tx_dma[nr]); + np->tx_ring.orig[nr].flaglen = cpu_to_le32((bcnt-1) | tx_flags); } else { - np->tx_ring.ex[nr].PacketBufferHigh = cpu_to_le64(np->tx_dma[nr]) >> 32; - np->tx_ring.ex[nr].PacketBufferLow = cpu_to_le64(np->tx_dma[nr]) & 0x0FFFFFFFF; - np->tx_ring.ex[nr].FlagLen = cpu_to_le32((bcnt-1) | tx_flags); + np->tx_ring.ex[nr].bufhigh = cpu_to_le64(np->tx_dma[nr]) >> 32; + np->tx_ring.ex[nr].buflow = cpu_to_le64(np->tx_dma[nr]) & 0x0FFFFFFFF; + np->tx_ring.ex[nr].flaglen = cpu_to_le32((bcnt-1) | tx_flags); } tx_flags = np->tx_flags; offset += bcnt; size -= bcnt; - } while(size); + } while (size); /* setup the fragments */ for (i = 0; i < fragments; i++) { @@ -1477,12 +1495,12 @@ static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev) np->tx_dma_len[nr] = bcnt; if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - np->tx_ring.orig[nr].PacketBuffer = cpu_to_le32(np->tx_dma[nr]); - np->tx_ring.orig[nr].FlagLen = cpu_to_le32((bcnt-1) | tx_flags); + np->tx_ring.orig[nr].buf = cpu_to_le32(np->tx_dma[nr]); + np->tx_ring.orig[nr].flaglen = cpu_to_le32((bcnt-1) | tx_flags); } else { - np->tx_ring.ex[nr].PacketBufferHigh = cpu_to_le64(np->tx_dma[nr]) >> 32; - np->tx_ring.ex[nr].PacketBufferLow = cpu_to_le64(np->tx_dma[nr]) & 0x0FFFFFFFF; - np->tx_ring.ex[nr].FlagLen = cpu_to_le32((bcnt-1) | tx_flags); + np->tx_ring.ex[nr].bufhigh = cpu_to_le64(np->tx_dma[nr]) >> 32; + np->tx_ring.ex[nr].buflow = cpu_to_le64(np->tx_dma[nr]) & 0x0FFFFFFFF; + np->tx_ring.ex[nr].flaglen = cpu_to_le32((bcnt-1) | tx_flags); } offset += bcnt; size -= bcnt; @@ -1491,9 +1509,9 @@ static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev) /* set last fragment flag */ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - np->tx_ring.orig[nr].FlagLen |= cpu_to_le32(tx_flags_extra); + np->tx_ring.orig[nr].flaglen |= cpu_to_le32(tx_flags_extra); } else { - np->tx_ring.ex[nr].FlagLen |= cpu_to_le32(tx_flags_extra); + np->tx_ring.ex[nr].flaglen |= cpu_to_le32(tx_flags_extra); } np->tx_skbuff[nr] = skb; @@ -1512,10 +1530,10 @@ static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev) /* set tx flags */ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - np->tx_ring.orig[start_nr].FlagLen |= cpu_to_le32(tx_flags | tx_flags_extra); + np->tx_ring.orig[start_nr].flaglen |= cpu_to_le32(tx_flags | tx_flags_extra); } else { - np->tx_ring.ex[start_nr].TxVlan = cpu_to_le32(tx_flags_vlan); - np->tx_ring.ex[start_nr].FlagLen |= cpu_to_le32(tx_flags | tx_flags_extra); + np->tx_ring.ex[start_nr].txvlan = cpu_to_le32(tx_flags_vlan); + np->tx_ring.ex[start_nr].flaglen |= cpu_to_le32(tx_flags | tx_flags_extra); } dprintk(KERN_DEBUG "%s: nv_start_xmit: packet %d (entries %d) queued for transmission. tx_flags_extra: %x\n", @@ -1547,7 +1565,7 @@ static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev) static void nv_tx_done(struct net_device *dev) { struct fe_priv *np = netdev_priv(dev); - u32 Flags; + u32 flags; unsigned int i; struct sk_buff *skb; @@ -1555,22 +1573,22 @@ static void nv_tx_done(struct net_device *dev) i = np->nic_tx % np->tx_ring_size; if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) - Flags = le32_to_cpu(np->tx_ring.orig[i].FlagLen); + flags = le32_to_cpu(np->tx_ring.orig[i].flaglen); else - Flags = le32_to_cpu(np->tx_ring.ex[i].FlagLen); + flags = le32_to_cpu(np->tx_ring.ex[i].flaglen); - dprintk(KERN_DEBUG "%s: nv_tx_done: looking at packet %d, Flags 0x%x.\n", - dev->name, np->nic_tx, Flags); - if (Flags & NV_TX_VALID) + dprintk(KERN_DEBUG "%s: nv_tx_done: looking at packet %d, flags 0x%x.\n", + dev->name, np->nic_tx, flags); + if (flags & NV_TX_VALID) break; if (np->desc_ver == DESC_VER_1) { - if (Flags & NV_TX_LASTPACKET) { + if (flags & NV_TX_LASTPACKET) { skb = np->tx_skbuff[i]; - if (Flags & (NV_TX_RETRYERROR|NV_TX_CARRIERLOST|NV_TX_LATECOLLISION| + if (flags & (NV_TX_RETRYERROR|NV_TX_CARRIERLOST|NV_TX_LATECOLLISION| NV_TX_UNDERFLOW|NV_TX_ERROR)) { - if (Flags & NV_TX_UNDERFLOW) + if (flags & NV_TX_UNDERFLOW) np->stats.tx_fifo_errors++; - if (Flags & NV_TX_CARRIERLOST) + if (flags & NV_TX_CARRIERLOST) np->stats.tx_carrier_errors++; np->stats.tx_errors++; } else { @@ -1579,13 +1597,13 @@ static void nv_tx_done(struct net_device *dev) } } } else { - if (Flags & NV_TX2_LASTPACKET) { + if (flags & NV_TX2_LASTPACKET) { skb = np->tx_skbuff[i]; - if (Flags & (NV_TX2_RETRYERROR|NV_TX2_CARRIERLOST|NV_TX2_LATECOLLISION| + if (flags & (NV_TX2_RETRYERROR|NV_TX2_CARRIERLOST|NV_TX2_LATECOLLISION| NV_TX2_UNDERFLOW|NV_TX2_ERROR)) { - if (Flags & NV_TX2_UNDERFLOW) + if (flags & NV_TX2_UNDERFLOW) np->stats.tx_fifo_errors++; - if (Flags & NV_TX2_CARRIERLOST) + if (flags & NV_TX2_CARRIERLOST) np->stats.tx_carrier_errors++; np->stats.tx_errors++; } else { @@ -1638,29 +1656,29 @@ static void nv_tx_timeout(struct net_device *dev) if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { printk(KERN_INFO "%03x: %08x %08x // %08x %08x // %08x %08x // %08x %08x\n", i, - le32_to_cpu(np->tx_ring.orig[i].PacketBuffer), - le32_to_cpu(np->tx_ring.orig[i].FlagLen), - le32_to_cpu(np->tx_ring.orig[i+1].PacketBuffer), - le32_to_cpu(np->tx_ring.orig[i+1].FlagLen), - le32_to_cpu(np->tx_ring.orig[i+2].PacketBuffer), - le32_to_cpu(np->tx_ring.orig[i+2].FlagLen), - le32_to_cpu(np->tx_ring.orig[i+3].PacketBuffer), - le32_to_cpu(np->tx_ring.orig[i+3].FlagLen)); + le32_to_cpu(np->tx_ring.orig[i].buf), + le32_to_cpu(np->tx_ring.orig[i].flaglen), + le32_to_cpu(np->tx_ring.orig[i+1].buf), + le32_to_cpu(np->tx_ring.orig[i+1].flaglen), + le32_to_cpu(np->tx_ring.orig[i+2].buf), + le32_to_cpu(np->tx_ring.orig[i+2].flaglen), + le32_to_cpu(np->tx_ring.orig[i+3].buf), + le32_to_cpu(np->tx_ring.orig[i+3].flaglen)); } else { printk(KERN_INFO "%03x: %08x %08x %08x // %08x %08x %08x // %08x %08x %08x // %08x %08x %08x\n", i, - le32_to_cpu(np->tx_ring.ex[i].PacketBufferHigh), - le32_to_cpu(np->tx_ring.ex[i].PacketBufferLow), - le32_to_cpu(np->tx_ring.ex[i].FlagLen), - le32_to_cpu(np->tx_ring.ex[i+1].PacketBufferHigh), - le32_to_cpu(np->tx_ring.ex[i+1].PacketBufferLow), - le32_to_cpu(np->tx_ring.ex[i+1].FlagLen), - le32_to_cpu(np->tx_ring.ex[i+2].PacketBufferHigh), - le32_to_cpu(np->tx_ring.ex[i+2].PacketBufferLow), - le32_to_cpu(np->tx_ring.ex[i+2].FlagLen), - le32_to_cpu(np->tx_ring.ex[i+3].PacketBufferHigh), - le32_to_cpu(np->tx_ring.ex[i+3].PacketBufferLow), - le32_to_cpu(np->tx_ring.ex[i+3].FlagLen)); + le32_to_cpu(np->tx_ring.ex[i].bufhigh), + le32_to_cpu(np->tx_ring.ex[i].buflow), + le32_to_cpu(np->tx_ring.ex[i].flaglen), + le32_to_cpu(np->tx_ring.ex[i+1].bufhigh), + le32_to_cpu(np->tx_ring.ex[i+1].buflow), + le32_to_cpu(np->tx_ring.ex[i+1].flaglen), + le32_to_cpu(np->tx_ring.ex[i+2].bufhigh), + le32_to_cpu(np->tx_ring.ex[i+2].buflow), + le32_to_cpu(np->tx_ring.ex[i+2].flaglen), + le32_to_cpu(np->tx_ring.ex[i+3].bufhigh), + le32_to_cpu(np->tx_ring.ex[i+3].buflow), + le32_to_cpu(np->tx_ring.ex[i+3].flaglen)); } } } @@ -1697,7 +1715,7 @@ static int nv_getlen(struct net_device *dev, void *packet, int datalen) int protolen; /* length as stored in the proto field */ /* 1) calculate len according to header */ - if ( ((struct vlan_ethhdr *)packet)->h_vlan_proto == __constant_htons(ETH_P_8021Q)) { + if ( ((struct vlan_ethhdr *)packet)->h_vlan_proto == htons(ETH_P_8021Q)) { protolen = ntohs( ((struct vlan_ethhdr *)packet)->h_vlan_encapsulated_proto ); hdrlen = VLAN_HLEN; } else { @@ -1740,13 +1758,14 @@ static int nv_getlen(struct net_device *dev, void *packet, int datalen) } } -static void nv_rx_process(struct net_device *dev) +static int nv_rx_process(struct net_device *dev, int limit) { struct fe_priv *np = netdev_priv(dev); - u32 Flags; + u32 flags; u32 vlanflags = 0; + int count; - for (;;) { + for (count = 0; count < limit; ++count) { struct sk_buff *skb; int len; int i; @@ -1755,18 +1774,18 @@ static void nv_rx_process(struct net_device *dev) i = np->cur_rx % np->rx_ring_size; if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - Flags = le32_to_cpu(np->rx_ring.orig[i].FlagLen); + flags = le32_to_cpu(np->rx_ring.orig[i].flaglen); len = nv_descr_getlength(&np->rx_ring.orig[i], np->desc_ver); } else { - Flags = le32_to_cpu(np->rx_ring.ex[i].FlagLen); + flags = le32_to_cpu(np->rx_ring.ex[i].flaglen); len = nv_descr_getlength_ex(&np->rx_ring.ex[i], np->desc_ver); - vlanflags = le32_to_cpu(np->rx_ring.ex[i].PacketBufferLow); + vlanflags = le32_to_cpu(np->rx_ring.ex[i].buflow); } - dprintk(KERN_DEBUG "%s: nv_rx_process: looking at packet %d, Flags 0x%x.\n", - dev->name, np->cur_rx, Flags); + dprintk(KERN_DEBUG "%s: nv_rx_process: looking at packet %d, flags 0x%x.\n", + dev->name, np->cur_rx, flags); - if (Flags & NV_RX_AVAIL) + if (flags & NV_RX_AVAIL) break; /* still owned by hardware, */ /* @@ -1780,7 +1799,7 @@ static void nv_rx_process(struct net_device *dev) { int j; - dprintk(KERN_DEBUG "Dumping packet (flags 0x%x).",Flags); + dprintk(KERN_DEBUG "Dumping packet (flags 0x%x).",flags); for (j=0; j<64; j++) { if ((j%16) == 0) dprintk("\n%03x:", j); @@ -1790,30 +1809,30 @@ static void nv_rx_process(struct net_device *dev) } /* look at what we actually got: */ if (np->desc_ver == DESC_VER_1) { - if (!(Flags & NV_RX_DESCRIPTORVALID)) + if (!(flags & NV_RX_DESCRIPTORVALID)) goto next_pkt; - if (Flags & NV_RX_ERROR) { - if (Flags & NV_RX_MISSEDFRAME) { + if (flags & NV_RX_ERROR) { + 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)) { + if (flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3)) { np->stats.rx_errors++; goto next_pkt; } - if (Flags & NV_RX_CRCERR) { + if (flags & NV_RX_CRCERR) { np->stats.rx_crc_errors++; np->stats.rx_errors++; goto next_pkt; } - if (Flags & NV_RX_OVERFLOW) { + if (flags & NV_RX_OVERFLOW) { np->stats.rx_over_errors++; np->stats.rx_errors++; goto next_pkt; } - if (Flags & NV_RX_ERROR4) { + if (flags & NV_RX_ERROR4) { len = nv_getlen(dev, np->rx_skbuff[i]->data, len); if (len < 0) { np->stats.rx_errors++; @@ -1821,32 +1840,32 @@ static void nv_rx_process(struct net_device *dev) } } /* framing errors are soft errors. */ - if (Flags & NV_RX_FRAMINGERR) { - if (Flags & NV_RX_SUBSTRACT1) { + if (flags & NV_RX_FRAMINGERR) { + if (flags & NV_RX_SUBSTRACT1) { len--; } } } } else { - if (!(Flags & NV_RX2_DESCRIPTORVALID)) + if (!(flags & NV_RX2_DESCRIPTORVALID)) goto next_pkt; - if (Flags & NV_RX2_ERROR) { - if (Flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3)) { + if (flags & NV_RX2_ERROR) { + if (flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3)) { np->stats.rx_errors++; goto next_pkt; } - if (Flags & NV_RX2_CRCERR) { + if (flags & NV_RX2_CRCERR) { np->stats.rx_crc_errors++; np->stats.rx_errors++; goto next_pkt; } - if (Flags & NV_RX2_OVERFLOW) { + if (flags & NV_RX2_OVERFLOW) { np->stats.rx_over_errors++; np->stats.rx_errors++; goto next_pkt; } - if (Flags & NV_RX2_ERROR4) { + if (flags & NV_RX2_ERROR4) { len = nv_getlen(dev, np->rx_skbuff[i]->data, len); if (len < 0) { np->stats.rx_errors++; @@ -1854,17 +1873,17 @@ static void nv_rx_process(struct net_device *dev) } } /* framing errors are soft errors */ - if (Flags & NV_RX2_FRAMINGERR) { - if (Flags & NV_RX2_SUBSTRACT1) { + if (flags & NV_RX2_FRAMINGERR) { + if (flags & NV_RX2_SUBSTRACT1) { len--; } } } if (np->txrxctl_bits & NVREG_TXRXCTL_RXCHECK) { - Flags &= NV_RX2_CHECKSUMMASK; - if (Flags == NV_RX2_CHECKSUMOK1 || - Flags == NV_RX2_CHECKSUMOK2 || - Flags == NV_RX2_CHECKSUMOK3) { + flags &= NV_RX2_CHECKSUMMASK; + if (flags == NV_RX2_CHECKSUMOK1 || + flags == NV_RX2_CHECKSUMOK2 || + flags == NV_RX2_CHECKSUMOK3) { dprintk(KERN_DEBUG "%s: hw checksum hit!.\n", dev->name); np->rx_skbuff[i]->ip_summed = CHECKSUM_UNNECESSARY; } else { @@ -1880,17 +1899,27 @@ static void nv_rx_process(struct net_device *dev) skb->protocol = eth_type_trans(skb, dev); dprintk(KERN_DEBUG "%s: nv_rx_process: packet %d with %d bytes, proto %d accepted.\n", dev->name, np->cur_rx, len, skb->protocol); - if (np->vlangrp && (vlanflags & NV_RX3_VLAN_TAG_PRESENT)) { - vlan_hwaccel_rx(skb, np->vlangrp, vlanflags & NV_RX3_VLAN_TAG_MASK); - } else { +#ifdef CONFIG_FORCEDETH_NAPI + if (np->vlangrp && (vlanflags & NV_RX3_VLAN_TAG_PRESENT)) + vlan_hwaccel_receive_skb(skb, np->vlangrp, + vlanflags & NV_RX3_VLAN_TAG_MASK); + else + netif_receive_skb(skb); +#else + if (np->vlangrp && (vlanflags & NV_RX3_VLAN_TAG_PRESENT)) + vlan_hwaccel_rx(skb, np->vlangrp, + vlanflags & NV_RX3_VLAN_TAG_MASK); + else netif_rx(skb); - } +#endif dev->last_rx = jiffies; np->stats.rx_packets++; np->stats.rx_bytes += len; next_pkt: np->cur_rx++; } + + return count; } static void set_bufsize(struct net_device *dev) @@ -1990,7 +2019,7 @@ static int nv_set_mac_address(struct net_device *dev, void *addr) struct fe_priv *np = netdev_priv(dev); struct sockaddr *macaddr = (struct sockaddr*)addr; - if(!is_valid_ether_addr(macaddr->sa_data)) + if (!is_valid_ether_addr(macaddr->sa_data)) return -EADDRNOTAVAIL; /* synchronized against open : rtnl_lock() held by caller */ @@ -2283,20 +2312,20 @@ set_speed: lpa_pause = lpa & (LPA_PAUSE_CAP| LPA_PAUSE_ASYM); switch (adv_pause) { - case (ADVERTISE_PAUSE_CAP): + case ADVERTISE_PAUSE_CAP: if (lpa_pause & LPA_PAUSE_CAP) { pause_flags |= NV_PAUSEFRAME_RX_ENABLE; if (np->pause_flags & NV_PAUSEFRAME_TX_REQ) pause_flags |= NV_PAUSEFRAME_TX_ENABLE; } break; - case (ADVERTISE_PAUSE_ASYM): + case ADVERTISE_PAUSE_ASYM: if (lpa_pause == (LPA_PAUSE_CAP| LPA_PAUSE_ASYM)) { pause_flags |= NV_PAUSEFRAME_TX_ENABLE; } break; - case (ADVERTISE_PAUSE_CAP| ADVERTISE_PAUSE_ASYM): + case ADVERTISE_PAUSE_CAP| ADVERTISE_PAUSE_ASYM: if (lpa_pause & LPA_PAUSE_CAP) { pause_flags |= NV_PAUSEFRAME_RX_ENABLE; @@ -2376,14 +2405,6 @@ static irqreturn_t nv_nic_irq(int foo, void *data, struct pt_regs *regs) nv_tx_done(dev); spin_unlock(&np->lock); - 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); @@ -2403,6 +2424,29 @@ static irqreturn_t nv_nic_irq(int foo, void *data, struct pt_regs *regs) printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n", dev->name, events); } +#ifdef CONFIG_FORCEDETH_NAPI + if (events & NVREG_IRQ_RX_ALL) { + netif_rx_schedule(dev); + + /* Disable furthur receive irq's */ + spin_lock(&np->lock); + np->irqmask &= ~NVREG_IRQ_RX_ALL; + + if (np->msi_flags & NV_MSI_X_ENABLED) + writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask); + else + writel(np->irqmask, base + NvRegIrqMask); + spin_unlock(&np->lock); + } +#else + nv_rx_process(dev, dev->weight); + 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); + } +#endif if (i > max_interrupt_work) { spin_lock(&np->lock); /* disable interrupts on the nic */ @@ -2474,6 +2518,63 @@ static irqreturn_t nv_nic_irq_tx(int foo, void *data, struct pt_regs *regs) return IRQ_RETVAL(i); } +#ifdef CONFIG_FORCEDETH_NAPI +static int nv_napi_poll(struct net_device *dev, int *budget) +{ + int pkts, limit = min(*budget, dev->quota); + struct fe_priv *np = netdev_priv(dev); + u8 __iomem *base = get_hwbase(dev); + + pkts = nv_rx_process(dev, limit); + + if (nv_alloc_rx(dev)) { + spin_lock_irq(&np->lock); + if (!np->in_shutdown) + mod_timer(&np->oom_kick, jiffies + OOM_REFILL); + spin_unlock_irq(&np->lock); + } + + if (pkts < limit) { + /* all done, no more packets present */ + netif_rx_complete(dev); + + /* re-enable receive interrupts */ + spin_lock_irq(&np->lock); + np->irqmask |= NVREG_IRQ_RX_ALL; + if (np->msi_flags & NV_MSI_X_ENABLED) + writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask); + else + writel(np->irqmask, base + NvRegIrqMask); + spin_unlock_irq(&np->lock); + return 0; + } else { + /* used up our quantum, so reschedule */ + dev->quota -= pkts; + *budget -= pkts; + return 1; + } +} +#endif + +#ifdef CONFIG_FORCEDETH_NAPI +static irqreturn_t nv_nic_irq_rx(int foo, void *data, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) data; + u8 __iomem *base = get_hwbase(dev); + u32 events; + + events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_RX_ALL; + writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus); + + if (events) { + netif_rx_schedule(dev); + /* disable receive interrupts on the nic */ + writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask); + pci_push(base); + } + return IRQ_HANDLED; +} +#else static irqreturn_t nv_nic_irq_rx(int foo, void *data, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) data; @@ -2492,7 +2593,7 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data, struct pt_regs *regs) if (!(events & np->irqmask)) break; - nv_rx_process(dev); + nv_rx_process(dev, dev->weight); if (nv_alloc_rx(dev)) { spin_lock_irq(&np->lock); if (!np->in_shutdown) @@ -2514,12 +2615,12 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data, struct pt_regs *regs) spin_unlock_irq(&np->lock); break; } - } dprintk(KERN_DEBUG "%s: nv_nic_irq_rx completed\n", dev->name); return IRQ_RETVAL(i); } +#endif static irqreturn_t nv_nic_irq_other(int foo, void *data, struct pt_regs *regs) { @@ -3245,7 +3346,7 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri if (!rxtx_ring || !rx_skbuff || !rx_dma || !tx_skbuff || !tx_dma || !tx_dma_len) { /* fall back to old rings */ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - if(rxtx_ring) + if (rxtx_ring) pci_free_consistent(np->pci_dev, sizeof(struct ring_desc) * (ring->rx_pending + ring->tx_pending), rxtx_ring, ring_addr); } else { @@ -3481,7 +3582,7 @@ static int nv_get_stats_count(struct net_device *dev) struct fe_priv *np = netdev_priv(dev); if (np->driver_data & DEV_HAS_STATISTICS) - return (sizeof(struct nv_ethtool_stats)/sizeof(u64)); + return sizeof(struct nv_ethtool_stats)/sizeof(u64); else return 0; } @@ -3619,7 +3720,7 @@ static int nv_loopback_test(struct net_device *dev) struct sk_buff *tx_skb, *rx_skb; dma_addr_t test_dma_addr; u32 tx_flags_extra = (np->desc_ver == DESC_VER_1 ? NV_TX_LASTPACKET : NV_TX2_LASTPACKET); - u32 Flags; + u32 flags; int len, i, pkt_len; u8 *pkt_data; u32 filter_flags = 0; @@ -3663,12 +3764,12 @@ static int nv_loopback_test(struct net_device *dev) tx_skb->end-tx_skb->data, PCI_DMA_FROMDEVICE); if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - np->tx_ring.orig[0].PacketBuffer = cpu_to_le32(test_dma_addr); - np->tx_ring.orig[0].FlagLen = cpu_to_le32((pkt_len-1) | np->tx_flags | tx_flags_extra); + np->tx_ring.orig[0].buf = cpu_to_le32(test_dma_addr); + np->tx_ring.orig[0].flaglen = cpu_to_le32((pkt_len-1) | np->tx_flags | tx_flags_extra); } else { - np->tx_ring.ex[0].PacketBufferHigh = cpu_to_le64(test_dma_addr) >> 32; - np->tx_ring.ex[0].PacketBufferLow = cpu_to_le64(test_dma_addr) & 0x0FFFFFFFF; - np->tx_ring.ex[0].FlagLen = cpu_to_le32((pkt_len-1) | np->tx_flags | tx_flags_extra); + np->tx_ring.ex[0].bufhigh = cpu_to_le64(test_dma_addr) >> 32; + np->tx_ring.ex[0].buflow = cpu_to_le64(test_dma_addr) & 0x0FFFFFFFF; + np->tx_ring.ex[0].flaglen = cpu_to_le32((pkt_len-1) | np->tx_flags | tx_flags_extra); } writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl); pci_push(get_hwbase(dev)); @@ -3677,21 +3778,21 @@ static int nv_loopback_test(struct net_device *dev) /* check for rx of the packet */ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - Flags = le32_to_cpu(np->rx_ring.orig[0].FlagLen); + flags = le32_to_cpu(np->rx_ring.orig[0].flaglen); len = nv_descr_getlength(&np->rx_ring.orig[0], np->desc_ver); } else { - Flags = le32_to_cpu(np->rx_ring.ex[0].FlagLen); + flags = le32_to_cpu(np->rx_ring.ex[0].flaglen); len = nv_descr_getlength_ex(&np->rx_ring.ex[0], np->desc_ver); } - if (Flags & NV_RX_AVAIL) { + if (flags & NV_RX_AVAIL) { ret = 0; } else if (np->desc_ver == DESC_VER_1) { - if (Flags & NV_RX_ERROR) + if (flags & NV_RX_ERROR) ret = 0; } else { - if (Flags & NV_RX2_ERROR) { + if (flags & NV_RX2_ERROR) { ret = 0; } } @@ -3753,6 +3854,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64 if (test->flags & ETH_TEST_FL_OFFLINE) { if (netif_running(dev)) { netif_stop_queue(dev); + netif_poll_disable(dev); netif_tx_lock_bh(dev); spin_lock_irq(&np->lock); nv_disable_hw_interrupts(dev, np->irqmask); @@ -3811,6 +3913,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64 nv_start_rx(dev); nv_start_tx(dev); netif_start_queue(dev); + netif_poll_enable(dev); nv_enable_hw_interrupts(dev, np->irqmask); } } @@ -3895,10 +3998,9 @@ static int nv_open(struct net_device *dev) dprintk(KERN_DEBUG "nv_open: begin\n"); - /* 1) erase previous misconfiguration */ + /* erase previous misconfiguration */ if (np->driver_data & DEV_HAS_POWER_CNTRL) nv_mac_reset(dev); - /* 4.1-1: stop adapter: ignored, 4.3 seems to be overkill */ writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA); writel(0, base + NvRegMulticastAddrB); writel(0, base + NvRegMulticastMaskA); @@ -3913,26 +4015,22 @@ static int nv_open(struct net_device *dev) if (np->pause_flags & NV_PAUSEFRAME_TX_CAPABLE) writel(NVREG_TX_PAUSEFRAME_DISABLE, base + NvRegTxPauseFrame); - /* 2) initialize descriptor rings */ + /* initialize descriptor rings */ set_bufsize(dev); oom = nv_init_ring(dev); writel(0, base + NvRegLinkSpeed); - writel(0, base + NvRegUnknownTransmitterReg); + writel(readl(base + NvRegTransmitPoll) & NVREG_TRANSMITPOLL_MAC_ADDR_REV, base + NvRegTransmitPoll); nv_txrx_reset(dev); writel(0, base + NvRegUnknownSetupReg6); np->in_shutdown = 0; - /* 3) set mac address */ - nv_copy_mac_to_hw(dev); - - /* 4) give hw rings */ + /* give hw rings */ setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING); writel( ((np->rx_ring_size-1) << NVREG_RINGSZ_RXSHIFT) + ((np->tx_ring_size-1) << NVREG_RINGSZ_TXSHIFT), base + NvRegRingSizes); - /* 5) continue setup */ writel(np->linkspeed, base + NvRegLinkSpeed); if (np->desc_ver == DESC_VER_1) writel(NVREG_TX_WM_DESC1_DEFAULT, base + NvRegTxWatermark); @@ -3950,7 +4048,6 @@ static int nv_open(struct net_device *dev) writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus); writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus); - /* 6) continue setup */ writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1); writel(readl(base + NvRegTransmitterStatus), base + NvRegTransmitterStatus); writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags); @@ -4020,6 +4117,8 @@ static int nv_open(struct net_device *dev) nv_start_rx(dev); nv_start_tx(dev); netif_start_queue(dev); + netif_poll_enable(dev); + if (ret) { netif_carrier_on(dev); } else { @@ -4049,6 +4148,7 @@ static int nv_close(struct net_device *dev) spin_lock_irq(&np->lock); np->in_shutdown = 1; spin_unlock_irq(&np->lock); + netif_poll_disable(dev); synchronize_irq(dev->irq); del_timer_sync(&np->oom_kick); @@ -4076,12 +4176,6 @@ static int nv_close(struct net_device *dev) if (np->wolenabled) nv_start_rx(dev); - /* special op: write back the misordered MAC address - otherwise - * the next nv_probe would see a wrong address. - */ - writel(np->orig_mac[0], base + NvRegMacAddrA); - writel(np->orig_mac[1], base + NvRegMacAddrB); - /* FIXME: power down nic */ return 0; @@ -4094,7 +4188,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i unsigned long addr; u8 __iomem *base; int err, i; - u32 powerstate; + u32 powerstate, txreg; dev = alloc_etherdev(sizeof(struct fe_priv)); err = -ENOMEM; @@ -4270,6 +4364,10 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = nv_poll_controller; #endif + dev->weight = 64; +#ifdef CONFIG_FORCEDETH_NAPI + dev->poll = nv_napi_poll; +#endif SET_ETHTOOL_OPS(dev, &ops); dev->tx_timeout = nv_tx_timeout; dev->watchdog_timeo = NV_WATCHDOG_TIMEO; @@ -4281,12 +4379,30 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i np->orig_mac[0] = readl(base + NvRegMacAddrA); np->orig_mac[1] = readl(base + NvRegMacAddrB); - dev->dev_addr[0] = (np->orig_mac[1] >> 8) & 0xff; - dev->dev_addr[1] = (np->orig_mac[1] >> 0) & 0xff; - dev->dev_addr[2] = (np->orig_mac[0] >> 24) & 0xff; - dev->dev_addr[3] = (np->orig_mac[0] >> 16) & 0xff; - dev->dev_addr[4] = (np->orig_mac[0] >> 8) & 0xff; - dev->dev_addr[5] = (np->orig_mac[0] >> 0) & 0xff; + /* check the workaround bit for correct mac address order */ + txreg = readl(base + NvRegTransmitPoll); + if (txreg & NVREG_TRANSMITPOLL_MAC_ADDR_REV) { + /* mac address is already in correct order */ + dev->dev_addr[0] = (np->orig_mac[0] >> 0) & 0xff; + dev->dev_addr[1] = (np->orig_mac[0] >> 8) & 0xff; + dev->dev_addr[2] = (np->orig_mac[0] >> 16) & 0xff; + dev->dev_addr[3] = (np->orig_mac[0] >> 24) & 0xff; + dev->dev_addr[4] = (np->orig_mac[1] >> 0) & 0xff; + dev->dev_addr[5] = (np->orig_mac[1] >> 8) & 0xff; + } else { + /* need to reverse mac address to correct order */ + dev->dev_addr[0] = (np->orig_mac[1] >> 8) & 0xff; + dev->dev_addr[1] = (np->orig_mac[1] >> 0) & 0xff; + dev->dev_addr[2] = (np->orig_mac[0] >> 24) & 0xff; + dev->dev_addr[3] = (np->orig_mac[0] >> 16) & 0xff; + dev->dev_addr[4] = (np->orig_mac[0] >> 8) & 0xff; + dev->dev_addr[5] = (np->orig_mac[0] >> 0) & 0xff; + /* set permanent address to be correct aswell */ + np->orig_mac[0] = (dev->dev_addr[0] << 0) + (dev->dev_addr[1] << 8) + + (dev->dev_addr[2] << 16) + (dev->dev_addr[3] << 24); + np->orig_mac[1] = (dev->dev_addr[4] << 0) + (dev->dev_addr[5] << 8); + writel(txreg|NVREG_TRANSMITPOLL_MAC_ADDR_REV, base + NvRegTransmitPoll); + } memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); if (!is_valid_ether_addr(dev->perm_addr)) { @@ -4309,6 +4425,9 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i 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 mac address */ + nv_copy_mac_to_hw(dev); + /* disable WOL */ writel(0, base + NvRegWakeUpFlags); np->wolenabled = 0; @@ -4421,9 +4540,17 @@ out: static void __devexit nv_remove(struct pci_dev *pci_dev) { struct net_device *dev = pci_get_drvdata(pci_dev); + struct fe_priv *np = netdev_priv(dev); + u8 __iomem *base = get_hwbase(dev); unregister_netdev(dev); + /* special op: write back the misordered MAC address - otherwise + * the next nv_probe would see a wrong address. + */ + writel(np->orig_mac[0], base + NvRegMacAddrA); + writel(np->orig_mac[1], base + NvRegMacAddrB); + /* free all structures */ free_rings(dev); iounmap(get_hwbase(dev)); diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index 0ecebfc31f0..cc0dcc9bf63 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -654,11 +654,8 @@ static int pcnet_config(struct pcmcia_device *link) SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); if (info->flags & (IS_DL10019|IS_DL10022)) { - u_char id = inb(dev->base_addr + 0x1a); dev->do_ioctl = &ei_ioctl; mii_phy_probe(dev); - if ((id == 0x30) && !info->pna_phy && (info->eth_phy == 4)) - info->eth_phy = 0; } link->dev_node = &info->node; @@ -821,15 +818,6 @@ static void mdio_write(kio_addr_t addr, int phy_id, int loc, int value) } } -static void mdio_reset(kio_addr_t addr, int phy_id) -{ - outb_p(0x08, addr); - outb_p(0x0c, addr); - outb_p(0x08, addr); - outb_p(0x0c, addr); - outb_p(0x00, addr); -} - /*====================================================================== EEPROM access routines for DL10019 and DL10022 based cards @@ -942,7 +930,8 @@ static void set_misc_reg(struct net_device *dev) } if (info->flags & IS_DL10022) { if (info->flags & HAS_MII) { - mdio_reset(nic_base + DLINK_GPIO, info->eth_phy); + /* Advertise 100F, 100H, 10F, 10H */ + mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1); /* Restart MII autonegotiation */ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000); mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200); diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c new file mode 100644 index 00000000000..c729aeeb469 --- /dev/null +++ b/drivers/net/qla3xxx.c @@ -0,0 +1,3537 @@ +/* + * QLogic QLA3xxx NIC HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla3xxx for copyright and licensing details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/dmapool.h> +#include <linux/mempool.h> +#include <linux/spinlock.h> +#include <linux/kthread.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/ip.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/skbuff.h> +#include <linux/rtnetlink.h> +#include <linux/if_vlan.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/mm.h> + +#include "qla3xxx.h" + +#define DRV_NAME "qla3xxx" +#define DRV_STRING "QLogic ISP3XXX Network Driver" +#define DRV_VERSION "v2.02.00-k36" +#define PFX DRV_NAME " " + +static const char ql3xxx_driver_name[] = DRV_NAME; +static const char ql3xxx_driver_version[] = DRV_VERSION; + +MODULE_AUTHOR("QLogic Corporation"); +MODULE_DESCRIPTION("QLogic ISP3XXX Network Driver " DRV_VERSION " "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +static const u32 default_msg + = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK + | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN; + +static int debug = -1; /* defaults above */ +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +static int msi; +module_param(msi, int, 0); +MODULE_PARM_DESC(msi, "Turn on Message Signaled Interrupts."); + +static struct pci_device_id ql3xxx_pci_tbl[] __devinitdata = { + {PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QL3022_DEVICE_ID)}, + /* required last entry */ + {0,} +}; + +MODULE_DEVICE_TABLE(pci, ql3xxx_pci_tbl); + +/* + * Caller must take hw_lock. + */ +static int ql_sem_spinlock(struct ql3_adapter *qdev, + u32 sem_mask, u32 sem_bits) +{ + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + u32 value; + unsigned int seconds = 3; + + do { + writel((sem_mask | sem_bits), + &port_regs->CommonRegs.semaphoreReg); + value = readl(&port_regs->CommonRegs.semaphoreReg); + if ((value & (sem_mask >> 16)) == sem_bits) + return 0; + ssleep(1); + } while(--seconds); + return -1; +} + +static void ql_sem_unlock(struct ql3_adapter *qdev, u32 sem_mask) +{ + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + writel(sem_mask, &port_regs->CommonRegs.semaphoreReg); + readl(&port_regs->CommonRegs.semaphoreReg); +} + +static int ql_sem_lock(struct ql3_adapter *qdev, u32 sem_mask, u32 sem_bits) +{ + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + u32 value; + + writel((sem_mask | sem_bits), &port_regs->CommonRegs.semaphoreReg); + value = readl(&port_regs->CommonRegs.semaphoreReg); + return ((value & (sem_mask >> 16)) == sem_bits); +} + +/* + * Caller holds hw_lock. + */ +static int ql_wait_for_drvr_lock(struct ql3_adapter *qdev) +{ + int i = 0; + + while (1) { + if (!ql_sem_lock(qdev, + QL_DRVR_SEM_MASK, + (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) + * 2) << 1)) { + if (i < 10) { + ssleep(1); + i++; + } else { + printk(KERN_ERR PFX "%s: Timed out waiting for " + "driver lock...\n", + qdev->ndev->name); + return 0; + } + } else { + printk(KERN_DEBUG PFX + "%s: driver lock acquired.\n", + qdev->ndev->name); + return 1; + } + } +} + +static void ql_set_register_page(struct ql3_adapter *qdev, u32 page) +{ + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + + writel(((ISP_CONTROL_NP_MASK << 16) | page), + &port_regs->CommonRegs.ispControlStatus); + readl(&port_regs->CommonRegs.ispControlStatus); + qdev->current_page = page; +} + +static u32 ql_read_common_reg_l(struct ql3_adapter *qdev, + u32 __iomem * reg) +{ + u32 value; + unsigned long hw_flags; + + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + value = readl(reg); + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + + return value; +} + +static u32 ql_read_common_reg(struct ql3_adapter *qdev, + u32 __iomem * reg) +{ + return readl(reg); +} + +static u32 ql_read_page0_reg_l(struct ql3_adapter *qdev, u32 __iomem *reg) +{ + u32 value; + unsigned long hw_flags; + + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + + if (qdev->current_page != 0) + ql_set_register_page(qdev,0); + value = readl(reg); + + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + return value; +} + +static u32 ql_read_page0_reg(struct ql3_adapter *qdev, u32 __iomem *reg) +{ + if (qdev->current_page != 0) + ql_set_register_page(qdev,0); + return readl(reg); +} + +static void ql_write_common_reg_l(struct ql3_adapter *qdev, + u32 * reg, u32 value) +{ + unsigned long hw_flags; + + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + writel(value, (u32 *) reg); + readl(reg); + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + return; +} + +static void ql_write_common_reg(struct ql3_adapter *qdev, + u32 * reg, u32 value) +{ + writel(value, (u32 *) reg); + readl(reg); + return; +} + +static void ql_write_page0_reg(struct ql3_adapter *qdev, + u32 * reg, u32 value) +{ + if (qdev->current_page != 0) + ql_set_register_page(qdev,0); + writel(value, (u32 *) reg); + readl(reg); + return; +} + +/* + * Caller holds hw_lock. Only called during init. + */ +static void ql_write_page1_reg(struct ql3_adapter *qdev, + u32 * reg, u32 value) +{ + if (qdev->current_page != 1) + ql_set_register_page(qdev,1); + writel(value, (u32 *) reg); + readl(reg); + return; +} + +/* + * Caller holds hw_lock. Only called during init. + */ +static void ql_write_page2_reg(struct ql3_adapter *qdev, + u32 * reg, u32 value) +{ + if (qdev->current_page != 2) + ql_set_register_page(qdev,2); + writel(value, (u32 *) reg); + readl(reg); + return; +} + +static void ql_disable_interrupts(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + + ql_write_common_reg_l(qdev, &port_regs->CommonRegs.ispInterruptMaskReg, + (ISP_IMR_ENABLE_INT << 16)); + +} + +static void ql_enable_interrupts(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + + ql_write_common_reg_l(qdev, &port_regs->CommonRegs.ispInterruptMaskReg, + ((0xff << 16) | ISP_IMR_ENABLE_INT)); + +} + +static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev, + struct ql_rcv_buf_cb *lrg_buf_cb) +{ + u64 map; + lrg_buf_cb->next = NULL; + + if (qdev->lrg_buf_free_tail == NULL) { /* The list is empty */ + qdev->lrg_buf_free_head = qdev->lrg_buf_free_tail = lrg_buf_cb; + } else { + qdev->lrg_buf_free_tail->next = lrg_buf_cb; + qdev->lrg_buf_free_tail = lrg_buf_cb; + } + + if (!lrg_buf_cb->skb) { + lrg_buf_cb->skb = dev_alloc_skb(qdev->lrg_buffer_len); + if (unlikely(!lrg_buf_cb->skb)) { + printk(KERN_ERR PFX "%s: failed dev_alloc_skb().\n", + qdev->ndev->name); + qdev->lrg_buf_skb_check++; + } else { + /* + * We save some space to copy the ethhdr from first + * buffer + */ + skb_reserve(lrg_buf_cb->skb, QL_HEADER_SPACE); + map = pci_map_single(qdev->pdev, + lrg_buf_cb->skb->data, + qdev->lrg_buffer_len - + QL_HEADER_SPACE, + PCI_DMA_FROMDEVICE); + lrg_buf_cb->buf_phy_addr_low = + cpu_to_le32(LS_64BITS(map)); + lrg_buf_cb->buf_phy_addr_high = + cpu_to_le32(MS_64BITS(map)); + pci_unmap_addr_set(lrg_buf_cb, mapaddr, map); + pci_unmap_len_set(lrg_buf_cb, maplen, + qdev->lrg_buffer_len - + QL_HEADER_SPACE); + } + } + + qdev->lrg_buf_free_count++; +} + +static struct ql_rcv_buf_cb *ql_get_from_lrg_buf_free_list(struct ql3_adapter + *qdev) +{ + struct ql_rcv_buf_cb *lrg_buf_cb; + + if ((lrg_buf_cb = qdev->lrg_buf_free_head) != NULL) { + if ((qdev->lrg_buf_free_head = lrg_buf_cb->next) == NULL) + qdev->lrg_buf_free_tail = NULL; + qdev->lrg_buf_free_count--; + } + + return lrg_buf_cb; +} + +static u32 addrBits = EEPROM_NO_ADDR_BITS; +static u32 dataBits = EEPROM_NO_DATA_BITS; + +static void fm93c56a_deselect(struct ql3_adapter *qdev); +static void eeprom_readword(struct ql3_adapter *qdev, u32 eepromAddr, + unsigned short *value); + +/* + * Caller holds hw_lock. + */ +static void fm93c56a_select(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + + qdev->eeprom_cmd_data = AUBURN_EEPROM_CS_1; + ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev->eeprom_cmd_data); + ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg, + ((ISP_NVRAM_MASK << 16) | qdev->eeprom_cmd_data)); +} + +/* + * Caller holds hw_lock. + */ +static void fm93c56a_cmd(struct ql3_adapter *qdev, u32 cmd, u32 eepromAddr) +{ + int i; + u32 mask; + u32 dataBit; + u32 previousBit; + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + + /* Clock in a zero, then do the start bit */ + ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev->eeprom_cmd_data | + AUBURN_EEPROM_DO_1); + ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev-> + eeprom_cmd_data | AUBURN_EEPROM_DO_1 | + AUBURN_EEPROM_CLK_RISE); + ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev-> + eeprom_cmd_data | AUBURN_EEPROM_DO_1 | + AUBURN_EEPROM_CLK_FALL); + + mask = 1 << (FM93C56A_CMD_BITS - 1); + /* Force the previous data bit to be different */ + previousBit = 0xffff; + for (i = 0; i < FM93C56A_CMD_BITS; i++) { + dataBit = + (cmd & mask) ? AUBURN_EEPROM_DO_1 : AUBURN_EEPROM_DO_0; + if (previousBit != dataBit) { + /* + * If the bit changed, then change the DO state to + * match + */ + ql_write_common_reg(qdev, + &port_regs->CommonRegs. + serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev-> + eeprom_cmd_data | dataBit); + previousBit = dataBit; + } + ql_write_common_reg(qdev, + &port_regs->CommonRegs. + serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev-> + eeprom_cmd_data | dataBit | + AUBURN_EEPROM_CLK_RISE); + ql_write_common_reg(qdev, + &port_regs->CommonRegs. + serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev-> + eeprom_cmd_data | dataBit | + AUBURN_EEPROM_CLK_FALL); + cmd = cmd << 1; + } + + mask = 1 << (addrBits - 1); + /* Force the previous data bit to be different */ + previousBit = 0xffff; + for (i = 0; i < addrBits; i++) { + dataBit = + (eepromAddr & mask) ? AUBURN_EEPROM_DO_1 : + AUBURN_EEPROM_DO_0; + if (previousBit != dataBit) { + /* + * If the bit changed, then change the DO state to + * match + */ + ql_write_common_reg(qdev, + &port_regs->CommonRegs. + serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev-> + eeprom_cmd_data | dataBit); + previousBit = dataBit; + } + ql_write_common_reg(qdev, + &port_regs->CommonRegs. + serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev-> + eeprom_cmd_data | dataBit | + AUBURN_EEPROM_CLK_RISE); + ql_write_common_reg(qdev, + &port_regs->CommonRegs. + serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev-> + eeprom_cmd_data | dataBit | + AUBURN_EEPROM_CLK_FALL); + eepromAddr = eepromAddr << 1; + } +} + +/* + * Caller holds hw_lock. + */ +static void fm93c56a_deselect(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + qdev->eeprom_cmd_data = AUBURN_EEPROM_CS_0; + ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev->eeprom_cmd_data); +} + +/* + * Caller holds hw_lock. + */ +static void fm93c56a_datain(struct ql3_adapter *qdev, unsigned short *value) +{ + int i; + u32 data = 0; + u32 dataBit; + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + + /* Read the data bits */ + /* The first bit is a dummy. Clock right over it. */ + for (i = 0; i < dataBits; i++) { + ql_write_common_reg(qdev, + &port_regs->CommonRegs. + serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev->eeprom_cmd_data | + AUBURN_EEPROM_CLK_RISE); + ql_write_common_reg(qdev, + &port_regs->CommonRegs. + serialPortInterfaceReg, + ISP_NVRAM_MASK | qdev->eeprom_cmd_data | + AUBURN_EEPROM_CLK_FALL); + dataBit = + (ql_read_common_reg + (qdev, + &port_regs->CommonRegs. + serialPortInterfaceReg) & AUBURN_EEPROM_DI_1) ? 1 : 0; + data = (data << 1) | dataBit; + } + *value = (u16) data; +} + +/* + * Caller holds hw_lock. + */ +static void eeprom_readword(struct ql3_adapter *qdev, + u32 eepromAddr, unsigned short *value) +{ + fm93c56a_select(qdev); + fm93c56a_cmd(qdev, (int)FM93C56A_READ, eepromAddr); + fm93c56a_datain(qdev, value); + fm93c56a_deselect(qdev); +} + +static void ql_swap_mac_addr(u8 * macAddress) +{ +#ifdef __BIG_ENDIAN + u8 temp; + temp = macAddress[0]; + macAddress[0] = macAddress[1]; + macAddress[1] = temp; + temp = macAddress[2]; + macAddress[2] = macAddress[3]; + macAddress[3] = temp; + temp = macAddress[4]; + macAddress[4] = macAddress[5]; + macAddress[5] = temp; +#endif +} + +static int ql_get_nvram_params(struct ql3_adapter *qdev) +{ + u16 *pEEPROMData; + u16 checksum = 0; + u32 index; + unsigned long hw_flags; + + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + + pEEPROMData = (u16 *) & qdev->nvram_data; + qdev->eeprom_cmd_data = 0; + if(ql_sem_spinlock(qdev, QL_NVRAM_SEM_MASK, + (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * + 2) << 10)) { + printk(KERN_ERR PFX"%s: Failed ql_sem_spinlock().\n", + __func__); + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + return -1; + } + + for (index = 0; index < EEPROM_SIZE; index++) { + eeprom_readword(qdev, index, pEEPROMData); + checksum += *pEEPROMData; + pEEPROMData++; + } + ql_sem_unlock(qdev, QL_NVRAM_SEM_MASK); + + if (checksum != 0) { + printk(KERN_ERR PFX "%s: checksum should be zero, is %x!!\n", + qdev->ndev->name, checksum); + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + return -1; + } + + /* + * We have a problem with endianness for the MAC addresses + * and the two 8-bit values version, and numPorts. We + * have to swap them on big endian systems. + */ + ql_swap_mac_addr(qdev->nvram_data.funcCfg_fn0.macAddress); + ql_swap_mac_addr(qdev->nvram_data.funcCfg_fn1.macAddress); + ql_swap_mac_addr(qdev->nvram_data.funcCfg_fn2.macAddress); + ql_swap_mac_addr(qdev->nvram_data.funcCfg_fn3.macAddress); + pEEPROMData = (u16 *) & qdev->nvram_data.version; + *pEEPROMData = le16_to_cpu(*pEEPROMData); + + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + return checksum; +} + +static const u32 PHYAddr[2] = { + PORT0_PHY_ADDRESS, PORT1_PHY_ADDRESS +}; + +static int ql_wait_for_mii_ready(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 temp; + int count = 1000; + + while (count) { + temp = ql_read_page0_reg(qdev, &port_regs->macMIIStatusReg); + if (!(temp & MAC_MII_STATUS_BSY)) + return 0; + udelay(10); + count--; + } + return -1; +} + +static void ql_mii_enable_scan_mode(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 scanControl; + + if (qdev->numPorts > 1) { + /* Auto scan will cycle through multiple ports */ + scanControl = MAC_MII_CONTROL_AS | MAC_MII_CONTROL_SC; + } else { + scanControl = MAC_MII_CONTROL_SC; + } + + /* + * Scan register 1 of PHY/PETBI, + * Set up to scan both devices + * The autoscan starts from the first register, completes + * the last one before rolling over to the first + */ + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg, + PHYAddr[0] | MII_SCAN_REGISTER); + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg, + (scanControl) | + ((MAC_MII_CONTROL_SC | MAC_MII_CONTROL_AS) << 16)); +} + +static u8 ql_mii_disable_scan_mode(struct ql3_adapter *qdev) +{ + u8 ret; + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + + /* See if scan mode is enabled before we turn it off */ + if (ql_read_page0_reg(qdev, &port_regs->macMIIMgmtControlReg) & + (MAC_MII_CONTROL_AS | MAC_MII_CONTROL_SC)) { + /* Scan is enabled */ + ret = 1; + } else { + /* Scan is disabled */ + ret = 0; + } + + /* + * When disabling scan mode you must first change the MII register + * address + */ + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg, + PHYAddr[0] | MII_SCAN_REGISTER); + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg, + ((MAC_MII_CONTROL_SC | MAC_MII_CONTROL_AS | + MAC_MII_CONTROL_RC) << 16)); + + return ret; +} + +static int ql_mii_write_reg_ex(struct ql3_adapter *qdev, + u16 regAddr, u16 value, u32 mac_index) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u8 scanWasEnabled; + + scanWasEnabled = ql_mii_disable_scan_mode(qdev); + + if (ql_wait_for_mii_ready(qdev)) { + if (netif_msg_link(qdev)) + printk(KERN_WARNING PFX + "%s Timed out waiting for management port to " + "get free before issuing command.\n", + qdev->ndev->name); + return -1; + } + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg, + PHYAddr[mac_index] | regAddr); + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtDataReg, value); + + /* Wait for write to complete 9/10/04 SJP */ + if (ql_wait_for_mii_ready(qdev)) { + if (netif_msg_link(qdev)) + printk(KERN_WARNING PFX + "%s: Timed out waiting for management port to" + "get free before issuing command.\n", + qdev->ndev->name); + return -1; + } + + if (scanWasEnabled) + ql_mii_enable_scan_mode(qdev); + + return 0; +} + +static int ql_mii_read_reg_ex(struct ql3_adapter *qdev, u16 regAddr, + u16 * value, u32 mac_index) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u8 scanWasEnabled; + u32 temp; + + scanWasEnabled = ql_mii_disable_scan_mode(qdev); + + if (ql_wait_for_mii_ready(qdev)) { + if (netif_msg_link(qdev)) + printk(KERN_WARNING PFX + "%s: Timed out waiting for management port to " + "get free before issuing command.\n", + qdev->ndev->name); + return -1; + } + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg, + PHYAddr[mac_index] | regAddr); + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg, + (MAC_MII_CONTROL_RC << 16)); + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg, + (MAC_MII_CONTROL_RC << 16) | MAC_MII_CONTROL_RC); + + /* Wait for the read to complete */ + if (ql_wait_for_mii_ready(qdev)) { + if (netif_msg_link(qdev)) + printk(KERN_WARNING PFX + "%s: Timed out waiting for management port to " + "get free after issuing command.\n", + qdev->ndev->name); + return -1; + } + + temp = ql_read_page0_reg(qdev, &port_regs->macMIIMgmtDataReg); + *value = (u16) temp; + + if (scanWasEnabled) + ql_mii_enable_scan_mode(qdev); + + return 0; +} + +static int ql_mii_write_reg(struct ql3_adapter *qdev, u16 regAddr, u16 value) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + + ql_mii_disable_scan_mode(qdev); + + if (ql_wait_for_mii_ready(qdev)) { + if (netif_msg_link(qdev)) + printk(KERN_WARNING PFX + "%s: Timed out waiting for management port to " + "get free before issuing command.\n", + qdev->ndev->name); + return -1; + } + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg, + qdev->PHYAddr | regAddr); + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtDataReg, value); + + /* Wait for write to complete. */ + if (ql_wait_for_mii_ready(qdev)) { + if (netif_msg_link(qdev)) + printk(KERN_WARNING PFX + "%s: Timed out waiting for management port to " + "get free before issuing command.\n", + qdev->ndev->name); + return -1; + } + + ql_mii_enable_scan_mode(qdev); + + return 0; +} + +static int ql_mii_read_reg(struct ql3_adapter *qdev, u16 regAddr, u16 *value) +{ + u32 temp; + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + + ql_mii_disable_scan_mode(qdev); + + if (ql_wait_for_mii_ready(qdev)) { + if (netif_msg_link(qdev)) + printk(KERN_WARNING PFX + "%s: Timed out waiting for management port to " + "get free before issuing command.\n", + qdev->ndev->name); + return -1; + } + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg, + qdev->PHYAddr | regAddr); + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg, + (MAC_MII_CONTROL_RC << 16)); + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg, + (MAC_MII_CONTROL_RC << 16) | MAC_MII_CONTROL_RC); + + /* Wait for the read to complete */ + if (ql_wait_for_mii_ready(qdev)) { + if (netif_msg_link(qdev)) + printk(KERN_WARNING PFX + "%s: Timed out waiting for management port to " + "get free before issuing command.\n", + qdev->ndev->name); + return -1; + } + + temp = ql_read_page0_reg(qdev, &port_regs->macMIIMgmtDataReg); + *value = (u16) temp; + + ql_mii_enable_scan_mode(qdev); + + return 0; +} + +static void ql_petbi_reset(struct ql3_adapter *qdev) +{ + ql_mii_write_reg(qdev, PETBI_CONTROL_REG, PETBI_CTRL_SOFT_RESET); +} + +static void ql_petbi_start_neg(struct ql3_adapter *qdev) +{ + u16 reg; + + /* Enable Auto-negotiation sense */ + ql_mii_read_reg(qdev, PETBI_TBI_CTRL, ®); + reg |= PETBI_TBI_AUTO_SENSE; + ql_mii_write_reg(qdev, PETBI_TBI_CTRL, reg); + + ql_mii_write_reg(qdev, PETBI_NEG_ADVER, + PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX); + + ql_mii_write_reg(qdev, PETBI_CONTROL_REG, + PETBI_CTRL_AUTO_NEG | PETBI_CTRL_RESTART_NEG | + PETBI_CTRL_FULL_DUPLEX | PETBI_CTRL_SPEED_1000); + +} + +static void ql_petbi_reset_ex(struct ql3_adapter *qdev, u32 mac_index) +{ + ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG, PETBI_CTRL_SOFT_RESET, + mac_index); +} + +static void ql_petbi_start_neg_ex(struct ql3_adapter *qdev, u32 mac_index) +{ + u16 reg; + + /* Enable Auto-negotiation sense */ + ql_mii_read_reg_ex(qdev, PETBI_TBI_CTRL, ®, mac_index); + reg |= PETBI_TBI_AUTO_SENSE; + ql_mii_write_reg_ex(qdev, PETBI_TBI_CTRL, reg, mac_index); + + ql_mii_write_reg_ex(qdev, PETBI_NEG_ADVER, + PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX, mac_index); + + ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG, + PETBI_CTRL_AUTO_NEG | PETBI_CTRL_RESTART_NEG | + PETBI_CTRL_FULL_DUPLEX | PETBI_CTRL_SPEED_1000, + mac_index); +} + +static void ql_petbi_init(struct ql3_adapter *qdev) +{ + ql_petbi_reset(qdev); + ql_petbi_start_neg(qdev); +} + +static void ql_petbi_init_ex(struct ql3_adapter *qdev, u32 mac_index) +{ + ql_petbi_reset_ex(qdev, mac_index); + ql_petbi_start_neg_ex(qdev, mac_index); +} + +static int ql_is_petbi_neg_pause(struct ql3_adapter *qdev) +{ + u16 reg; + + if (ql_mii_read_reg(qdev, PETBI_NEG_PARTNER, ®) < 0) + return 0; + + return (reg & PETBI_NEG_PAUSE_MASK) == PETBI_NEG_PAUSE; +} + +static int ql_phy_get_speed(struct ql3_adapter *qdev) +{ + u16 reg; + + if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, ®) < 0) + return 0; + + reg = (((reg & 0x18) >> 3) & 3); + + if (reg == 2) + return SPEED_1000; + else if (reg == 1) + return SPEED_100; + else if (reg == 0) + return SPEED_10; + else + return -1; +} + +static int ql_is_full_dup(struct ql3_adapter *qdev) +{ + u16 reg; + + if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, ®) < 0) + return 0; + + return (reg & PHY_AUX_DUPLEX_STAT) != 0; +} + +static int ql_is_phy_neg_pause(struct ql3_adapter *qdev) +{ + u16 reg; + + if (ql_mii_read_reg(qdev, PHY_NEG_PARTNER, ®) < 0) + return 0; + + return (reg & PHY_NEG_PAUSE) != 0; +} + +/* + * Caller holds hw_lock. + */ +static void ql_mac_enable(struct ql3_adapter *qdev, u32 enable) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 value; + + if (enable) + value = (MAC_CONFIG_REG_PE | (MAC_CONFIG_REG_PE << 16)); + else + value = (MAC_CONFIG_REG_PE << 16); + + if (qdev->mac_index) + ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value); + else + ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value); +} + +/* + * Caller holds hw_lock. + */ +static void ql_mac_cfg_soft_reset(struct ql3_adapter *qdev, u32 enable) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 value; + + if (enable) + value = (MAC_CONFIG_REG_SR | (MAC_CONFIG_REG_SR << 16)); + else + value = (MAC_CONFIG_REG_SR << 16); + + if (qdev->mac_index) + ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value); + else + ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value); +} + +/* + * Caller holds hw_lock. + */ +static void ql_mac_cfg_gig(struct ql3_adapter *qdev, u32 enable) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 value; + + if (enable) + value = (MAC_CONFIG_REG_GM | (MAC_CONFIG_REG_GM << 16)); + else + value = (MAC_CONFIG_REG_GM << 16); + + if (qdev->mac_index) + ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value); + else + ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value); +} + +/* + * Caller holds hw_lock. + */ +static void ql_mac_cfg_full_dup(struct ql3_adapter *qdev, u32 enable) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 value; + + if (enable) + value = (MAC_CONFIG_REG_FD | (MAC_CONFIG_REG_FD << 16)); + else + value = (MAC_CONFIG_REG_FD << 16); + + if (qdev->mac_index) + ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value); + else + ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value); +} + +/* + * Caller holds hw_lock. + */ +static void ql_mac_cfg_pause(struct ql3_adapter *qdev, u32 enable) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 value; + + if (enable) + value = + ((MAC_CONFIG_REG_TF | MAC_CONFIG_REG_RF) | + ((MAC_CONFIG_REG_TF | MAC_CONFIG_REG_RF) << 16)); + else + value = ((MAC_CONFIG_REG_TF | MAC_CONFIG_REG_RF) << 16); + + if (qdev->mac_index) + ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value); + else + ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value); +} + +/* + * Caller holds hw_lock. + */ +static int ql_is_fiber(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 bitToCheck = 0; + u32 temp; + + switch (qdev->mac_index) { + case 0: + bitToCheck = PORT_STATUS_SM0; + break; + case 1: + bitToCheck = PORT_STATUS_SM1; + break; + } + + temp = ql_read_page0_reg(qdev, &port_regs->portStatus); + return (temp & bitToCheck) != 0; +} + +static int ql_is_auto_cfg(struct ql3_adapter *qdev) +{ + u16 reg; + ql_mii_read_reg(qdev, 0x00, ®); + return (reg & 0x1000) != 0; +} + +/* + * Caller holds hw_lock. + */ +static int ql_is_auto_neg_complete(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 bitToCheck = 0; + u32 temp; + + switch (qdev->mac_index) { + case 0: + bitToCheck = PORT_STATUS_AC0; + break; + case 1: + bitToCheck = PORT_STATUS_AC1; + break; + } + + temp = ql_read_page0_reg(qdev, &port_regs->portStatus); + if (temp & bitToCheck) { + if (netif_msg_link(qdev)) + printk(KERN_INFO PFX + "%s: Auto-Negotiate complete.\n", + qdev->ndev->name); + return 1; + } else { + if (netif_msg_link(qdev)) + printk(KERN_WARNING PFX + "%s: Auto-Negotiate incomplete.\n", + qdev->ndev->name); + return 0; + } +} + +/* + * ql_is_neg_pause() returns 1 if pause was negotiated to be on + */ +static int ql_is_neg_pause(struct ql3_adapter *qdev) +{ + if (ql_is_fiber(qdev)) + return ql_is_petbi_neg_pause(qdev); + else + return ql_is_phy_neg_pause(qdev); +} + +static int ql_auto_neg_error(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 bitToCheck = 0; + u32 temp; + + switch (qdev->mac_index) { + case 0: + bitToCheck = PORT_STATUS_AE0; + break; + case 1: + bitToCheck = PORT_STATUS_AE1; + break; + } + temp = ql_read_page0_reg(qdev, &port_regs->portStatus); + return (temp & bitToCheck) != 0; +} + +static u32 ql_get_link_speed(struct ql3_adapter *qdev) +{ + if (ql_is_fiber(qdev)) + return SPEED_1000; + else + return ql_phy_get_speed(qdev); +} + +static int ql_is_link_full_dup(struct ql3_adapter *qdev) +{ + if (ql_is_fiber(qdev)) + return 1; + else + return ql_is_full_dup(qdev); +} + +/* + * Caller holds hw_lock. + */ +static int ql_link_down_detect(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 bitToCheck = 0; + u32 temp; + + switch (qdev->mac_index) { + case 0: + bitToCheck = ISP_CONTROL_LINK_DN_0; + break; + case 1: + bitToCheck = ISP_CONTROL_LINK_DN_1; + break; + } + + temp = + ql_read_common_reg(qdev, &port_regs->CommonRegs.ispControlStatus); + return (temp & bitToCheck) != 0; +} + +/* + * Caller holds hw_lock. + */ +static int ql_link_down_detect_clear(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + + switch (qdev->mac_index) { + case 0: + ql_write_common_reg(qdev, + &port_regs->CommonRegs.ispControlStatus, + (ISP_CONTROL_LINK_DN_0) | + (ISP_CONTROL_LINK_DN_0 << 16)); + break; + + case 1: + ql_write_common_reg(qdev, + &port_regs->CommonRegs.ispControlStatus, + (ISP_CONTROL_LINK_DN_1) | + (ISP_CONTROL_LINK_DN_1 << 16)); + break; + + default: + return 1; + } + + return 0; +} + +/* + * Caller holds hw_lock. + */ +static int ql_this_adapter_controls_port(struct ql3_adapter *qdev, + u32 mac_index) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 bitToCheck = 0; + u32 temp; + + switch (mac_index) { + case 0: + bitToCheck = PORT_STATUS_F1_ENABLED; + break; + case 1: + bitToCheck = PORT_STATUS_F3_ENABLED; + break; + default: + break; + } + + temp = ql_read_page0_reg(qdev, &port_regs->portStatus); + if (temp & bitToCheck) { + if (netif_msg_link(qdev)) + printk(KERN_DEBUG PFX + "%s: is not link master.\n", qdev->ndev->name); + return 0; + } else { + if (netif_msg_link(qdev)) + printk(KERN_DEBUG PFX + "%s: is link master.\n", qdev->ndev->name); + return 1; + } +} + +static void ql_phy_reset_ex(struct ql3_adapter *qdev, u32 mac_index) +{ + ql_mii_write_reg_ex(qdev, CONTROL_REG, PHY_CTRL_SOFT_RESET, mac_index); +} + +static void ql_phy_start_neg_ex(struct ql3_adapter *qdev, u32 mac_index) +{ + u16 reg; + + ql_mii_write_reg_ex(qdev, PHY_NEG_ADVER, + PHY_NEG_PAUSE | PHY_NEG_ADV_SPEED | 1, mac_index); + + ql_mii_read_reg_ex(qdev, CONTROL_REG, ®, mac_index); + ql_mii_write_reg_ex(qdev, CONTROL_REG, reg | PHY_CTRL_RESTART_NEG, + mac_index); +} + +static void ql_phy_init_ex(struct ql3_adapter *qdev, u32 mac_index) +{ + ql_phy_reset_ex(qdev, mac_index); + ql_phy_start_neg_ex(qdev, mac_index); +} + +/* + * Caller holds hw_lock. + */ +static u32 ql_get_link_state(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + u32 bitToCheck = 0; + u32 temp, linkState; + + switch (qdev->mac_index) { + case 0: + bitToCheck = PORT_STATUS_UP0; + break; + case 1: + bitToCheck = PORT_STATUS_UP1; + break; + } + temp = ql_read_page0_reg(qdev, &port_regs->portStatus); + if (temp & bitToCheck) { + linkState = LS_UP; + } else { + linkState = LS_DOWN; + if (netif_msg_link(qdev)) + printk(KERN_WARNING PFX + "%s: Link is down.\n", qdev->ndev->name); + } + return linkState; +} + +static int ql_port_start(struct ql3_adapter *qdev) +{ + if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, + (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * + 2) << 7)) + return -1; + + if (ql_is_fiber(qdev)) { + ql_petbi_init(qdev); + } else { + /* Copper port */ + ql_phy_init_ex(qdev, qdev->mac_index); + } + + ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); + return 0; +} + +static int ql_finish_auto_neg(struct ql3_adapter *qdev) +{ + + if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, + (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * + 2) << 7)) + return -1; + + if (!ql_auto_neg_error(qdev)) { + if (test_bit(QL_LINK_MASTER,&qdev->flags)) { + /* configure the MAC */ + if (netif_msg_link(qdev)) + printk(KERN_DEBUG PFX + "%s: Configuring link.\n", + qdev->ndev-> + name); + ql_mac_cfg_soft_reset(qdev, 1); + ql_mac_cfg_gig(qdev, + (ql_get_link_speed + (qdev) == + SPEED_1000)); + ql_mac_cfg_full_dup(qdev, + ql_is_link_full_dup + (qdev)); + ql_mac_cfg_pause(qdev, + ql_is_neg_pause + (qdev)); + ql_mac_cfg_soft_reset(qdev, 0); + + /* enable the MAC */ + if (netif_msg_link(qdev)) + printk(KERN_DEBUG PFX + "%s: Enabling mac.\n", + qdev->ndev-> + name); + ql_mac_enable(qdev, 1); + } + + if (netif_msg_link(qdev)) + printk(KERN_DEBUG PFX + "%s: Change port_link_state LS_DOWN to LS_UP.\n", + qdev->ndev->name); + qdev->port_link_state = LS_UP; + netif_start_queue(qdev->ndev); + netif_carrier_on(qdev->ndev); + if (netif_msg_link(qdev)) + printk(KERN_INFO PFX + "%s: Link is up at %d Mbps, %s duplex.\n", + qdev->ndev->name, + ql_get_link_speed(qdev), + ql_is_link_full_dup(qdev) + ? "full" : "half"); + + } else { /* Remote error detected */ + + if (test_bit(QL_LINK_MASTER,&qdev->flags)) { + if (netif_msg_link(qdev)) + printk(KERN_DEBUG PFX + "%s: Remote error detected. " + "Calling ql_port_start().\n", + qdev->ndev-> + name); + /* + * ql_port_start() is shared code and needs + * to lock the PHY on it's own. + */ + ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); + if(ql_port_start(qdev)) {/* Restart port */ + return -1; + } else + return 0; + } + } + ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); + return 0; +} + +static void ql_link_state_machine(struct ql3_adapter *qdev) +{ + u32 curr_link_state; + unsigned long hw_flags; + + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + + curr_link_state = ql_get_link_state(qdev); + + if (test_bit(QL_RESET_ACTIVE,&qdev->flags)) { + if (netif_msg_link(qdev)) + printk(KERN_INFO PFX + "%s: Reset in progress, skip processing link " + "state.\n", qdev->ndev->name); + return; + } + + switch (qdev->port_link_state) { + default: + if (test_bit(QL_LINK_MASTER,&qdev->flags)) { + ql_port_start(qdev); + } + qdev->port_link_state = LS_DOWN; + /* Fall Through */ + + case LS_DOWN: + if (netif_msg_link(qdev)) + printk(KERN_DEBUG PFX + "%s: port_link_state = LS_DOWN.\n", + qdev->ndev->name); + if (curr_link_state == LS_UP) { + if (netif_msg_link(qdev)) + printk(KERN_DEBUG PFX + "%s: curr_link_state = LS_UP.\n", + qdev->ndev->name); + if (ql_is_auto_neg_complete(qdev)) + ql_finish_auto_neg(qdev); + + if (qdev->port_link_state == LS_UP) + ql_link_down_detect_clear(qdev); + + } + break; + + case LS_UP: + /* + * See if the link is currently down or went down and came + * back up + */ + if ((curr_link_state == LS_DOWN) || ql_link_down_detect(qdev)) { + if (netif_msg_link(qdev)) + printk(KERN_INFO PFX "%s: Link is down.\n", + qdev->ndev->name); + qdev->port_link_state = LS_DOWN; + } + break; + } + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); +} + +/* + * Caller must take hw_lock and QL_PHY_GIO_SEM. + */ +static void ql_get_phy_owner(struct ql3_adapter *qdev) +{ + if (ql_this_adapter_controls_port(qdev, qdev->mac_index)) + set_bit(QL_LINK_MASTER,&qdev->flags); + else + clear_bit(QL_LINK_MASTER,&qdev->flags); +} + +/* + * Caller must take hw_lock and QL_PHY_GIO_SEM. + */ +static void ql_init_scan_mode(struct ql3_adapter *qdev) +{ + ql_mii_enable_scan_mode(qdev); + + if (test_bit(QL_LINK_OPTICAL,&qdev->flags)) { + if (ql_this_adapter_controls_port(qdev, qdev->mac_index)) + ql_petbi_init_ex(qdev, qdev->mac_index); + } else { + if (ql_this_adapter_controls_port(qdev, qdev->mac_index)) + ql_phy_init_ex(qdev, qdev->mac_index); + } +} + +/* + * MII_Setup needs to be called before taking the PHY out of reset so that the + * management interface clock speed can be set properly. It would be better if + * we had a way to disable MDC until after the PHY is out of reset, but we + * don't have that capability. + */ +static int ql_mii_setup(struct ql3_adapter *qdev) +{ + u32 reg; + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + + if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, + (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * + 2) << 7)) + return -1; + + /* Divide 125MHz clock by 28 to meet PHY timing requirements */ + reg = MAC_MII_CONTROL_CLK_SEL_DIV28; + + ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg, + reg | ((MAC_MII_CONTROL_CLK_SEL_MASK) << 16)); + + ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); + return 0; +} + +static u32 ql_supported_modes(struct ql3_adapter *qdev) +{ + u32 supported; + + if (test_bit(QL_LINK_OPTICAL,&qdev->flags)) { + supported = SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE + | SUPPORTED_Autoneg; + } else { + supported = SUPPORTED_10baseT_Half + | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full + | SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full + | SUPPORTED_Autoneg | SUPPORTED_TP; + } + + return supported; +} + +static int ql_get_auto_cfg_status(struct ql3_adapter *qdev) +{ + int status; + unsigned long hw_flags; + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, + (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * + 2) << 7)) + return 0; + status = ql_is_auto_cfg(qdev); + ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + return status; +} + +static u32 ql_get_speed(struct ql3_adapter *qdev) +{ + u32 status; + unsigned long hw_flags; + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, + (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * + 2) << 7)) + return 0; + status = ql_get_link_speed(qdev); + ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + return status; +} + +static int ql_get_full_dup(struct ql3_adapter *qdev) +{ + int status; + unsigned long hw_flags; + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, + (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * + 2) << 7)) + return 0; + status = ql_is_link_full_dup(qdev); + ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + return status; +} + + +static int ql_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) +{ + struct ql3_adapter *qdev = netdev_priv(ndev); + + ecmd->transceiver = XCVR_INTERNAL; + ecmd->supported = ql_supported_modes(qdev); + + if (test_bit(QL_LINK_OPTICAL,&qdev->flags)) { + ecmd->port = PORT_FIBRE; + } else { + ecmd->port = PORT_TP; + ecmd->phy_address = qdev->PHYAddr; + } + ecmd->advertising = ql_supported_modes(qdev); + ecmd->autoneg = ql_get_auto_cfg_status(qdev); + ecmd->speed = ql_get_speed(qdev); + ecmd->duplex = ql_get_full_dup(qdev); + return 0; +} + +static void ql_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *drvinfo) +{ + struct ql3_adapter *qdev = netdev_priv(ndev); + strncpy(drvinfo->driver, ql3xxx_driver_name, 32); + strncpy(drvinfo->version, ql3xxx_driver_version, 32); + strncpy(drvinfo->fw_version, "N/A", 32); + strncpy(drvinfo->bus_info, pci_name(qdev->pdev), 32); + drvinfo->n_stats = 0; + drvinfo->testinfo_len = 0; + drvinfo->regdump_len = 0; + drvinfo->eedump_len = 0; +} + +static u32 ql_get_msglevel(struct net_device *ndev) +{ + struct ql3_adapter *qdev = netdev_priv(ndev); + return qdev->msg_enable; +} + +static void ql_set_msglevel(struct net_device *ndev, u32 value) +{ + struct ql3_adapter *qdev = netdev_priv(ndev); + qdev->msg_enable = value; +} + +static struct ethtool_ops ql3xxx_ethtool_ops = { + .get_settings = ql_get_settings, + .get_drvinfo = ql_get_drvinfo, + .get_perm_addr = ethtool_op_get_perm_addr, + .get_link = ethtool_op_get_link, + .get_msglevel = ql_get_msglevel, + .set_msglevel = ql_set_msglevel, +}; + +static int ql_populate_free_queue(struct ql3_adapter *qdev) +{ + struct ql_rcv_buf_cb *lrg_buf_cb = qdev->lrg_buf_free_head; + u64 map; + + while (lrg_buf_cb) { + if (!lrg_buf_cb->skb) { + lrg_buf_cb->skb = dev_alloc_skb(qdev->lrg_buffer_len); + if (unlikely(!lrg_buf_cb->skb)) { + printk(KERN_DEBUG PFX + "%s: Failed dev_alloc_skb().\n", + qdev->ndev->name); + break; + } else { + /* + * We save some space to copy the ethhdr from + * first buffer + */ + skb_reserve(lrg_buf_cb->skb, QL_HEADER_SPACE); + map = pci_map_single(qdev->pdev, + lrg_buf_cb->skb->data, + qdev->lrg_buffer_len - + QL_HEADER_SPACE, + PCI_DMA_FROMDEVICE); + lrg_buf_cb->buf_phy_addr_low = + cpu_to_le32(LS_64BITS(map)); + lrg_buf_cb->buf_phy_addr_high = + cpu_to_le32(MS_64BITS(map)); + pci_unmap_addr_set(lrg_buf_cb, mapaddr, map); + pci_unmap_len_set(lrg_buf_cb, maplen, + qdev->lrg_buffer_len - + QL_HEADER_SPACE); + --qdev->lrg_buf_skb_check; + if (!qdev->lrg_buf_skb_check) + return 1; + } + } + lrg_buf_cb = lrg_buf_cb->next; + } + return 0; +} + +/* + * Caller holds hw_lock. + */ +static void ql_update_lrg_bufq_prod_index(struct ql3_adapter *qdev) +{ + struct bufq_addr_element *lrg_buf_q_ele; + int i; + struct ql_rcv_buf_cb *lrg_buf_cb; + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + + if ((qdev->lrg_buf_free_count >= 8) + && (qdev->lrg_buf_release_cnt >= 16)) { + + if (qdev->lrg_buf_skb_check) + if (!ql_populate_free_queue(qdev)) + return; + + lrg_buf_q_ele = qdev->lrg_buf_next_free; + + while ((qdev->lrg_buf_release_cnt >= 16) + && (qdev->lrg_buf_free_count >= 8)) { + + for (i = 0; i < 8; i++) { + lrg_buf_cb = + ql_get_from_lrg_buf_free_list(qdev); + lrg_buf_q_ele->addr_high = + lrg_buf_cb->buf_phy_addr_high; + lrg_buf_q_ele->addr_low = + lrg_buf_cb->buf_phy_addr_low; + lrg_buf_q_ele++; + + qdev->lrg_buf_release_cnt--; + } + + qdev->lrg_buf_q_producer_index++; + + if (qdev->lrg_buf_q_producer_index == NUM_LBUFQ_ENTRIES) + qdev->lrg_buf_q_producer_index = 0; + + if (qdev->lrg_buf_q_producer_index == + (NUM_LBUFQ_ENTRIES - 1)) { + lrg_buf_q_ele = qdev->lrg_buf_q_virt_addr; + } + } + + qdev->lrg_buf_next_free = lrg_buf_q_ele; + + ql_write_common_reg(qdev, + (u32 *) & port_regs->CommonRegs. + rxLargeQProducerIndex, + qdev->lrg_buf_q_producer_index); + } +} + +static void ql_process_mac_tx_intr(struct ql3_adapter *qdev, + struct ob_mac_iocb_rsp *mac_rsp) +{ + struct ql_tx_buf_cb *tx_cb; + + tx_cb = &qdev->tx_buf[mac_rsp->transaction_id]; + pci_unmap_single(qdev->pdev, + pci_unmap_addr(tx_cb, mapaddr), + pci_unmap_len(tx_cb, maplen), PCI_DMA_TODEVICE); + dev_kfree_skb_irq(tx_cb->skb); + qdev->stats.tx_packets++; + qdev->stats.tx_bytes += tx_cb->skb->len; + tx_cb->skb = NULL; + atomic_inc(&qdev->tx_count); +} + +static void ql_process_mac_rx_intr(struct ql3_adapter *qdev, + struct ib_mac_iocb_rsp *ib_mac_rsp_ptr) +{ + long int offset; + u32 lrg_buf_phy_addr_low = 0; + struct ql_rcv_buf_cb *lrg_buf_cb1 = NULL; + struct ql_rcv_buf_cb *lrg_buf_cb2 = NULL; + u32 *curr_ial_ptr; + struct sk_buff *skb; + u16 length = le16_to_cpu(ib_mac_rsp_ptr->length); + + /* + * Get the inbound address list (small buffer). + */ + offset = qdev->small_buf_index * QL_SMALL_BUFFER_SIZE; + if (++qdev->small_buf_index == NUM_SMALL_BUFFERS) + qdev->small_buf_index = 0; + + curr_ial_ptr = (u32 *) (qdev->small_buf_virt_addr + offset); + qdev->last_rsp_offset = qdev->small_buf_phy_addr_low + offset; + qdev->small_buf_release_cnt++; + + /* start of first buffer */ + lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); + lrg_buf_cb1 = &qdev->lrg_buf[qdev->lrg_buf_index]; + qdev->lrg_buf_release_cnt++; + if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) + qdev->lrg_buf_index = 0; + curr_ial_ptr++; /* 64-bit pointers require two incs. */ + curr_ial_ptr++; + + /* start of second buffer */ + lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); + lrg_buf_cb2 = &qdev->lrg_buf[qdev->lrg_buf_index]; + + /* + * Second buffer gets sent up the stack. + */ + qdev->lrg_buf_release_cnt++; + if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) + qdev->lrg_buf_index = 0; + skb = lrg_buf_cb2->skb; + + qdev->stats.rx_packets++; + qdev->stats.rx_bytes += length; + + skb_put(skb, length); + pci_unmap_single(qdev->pdev, + pci_unmap_addr(lrg_buf_cb2, mapaddr), + pci_unmap_len(lrg_buf_cb2, maplen), + PCI_DMA_FROMDEVICE); + prefetch(skb->data); + skb->dev = qdev->ndev; + skb->ip_summed = CHECKSUM_NONE; + skb->protocol = eth_type_trans(skb, qdev->ndev); + + netif_receive_skb(skb); + qdev->ndev->last_rx = jiffies; + lrg_buf_cb2->skb = NULL; + + ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1); + ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb2); +} + +static void ql_process_macip_rx_intr(struct ql3_adapter *qdev, + struct ib_ip_iocb_rsp *ib_ip_rsp_ptr) +{ + long int offset; + u32 lrg_buf_phy_addr_low = 0; + struct ql_rcv_buf_cb *lrg_buf_cb1 = NULL; + struct ql_rcv_buf_cb *lrg_buf_cb2 = NULL; + u32 *curr_ial_ptr; + struct sk_buff *skb1, *skb2; + struct net_device *ndev = qdev->ndev; + u16 length = le16_to_cpu(ib_ip_rsp_ptr->length); + u16 size = 0; + + /* + * Get the inbound address list (small buffer). + */ + + offset = qdev->small_buf_index * QL_SMALL_BUFFER_SIZE; + if (++qdev->small_buf_index == NUM_SMALL_BUFFERS) + qdev->small_buf_index = 0; + curr_ial_ptr = (u32 *) (qdev->small_buf_virt_addr + offset); + qdev->last_rsp_offset = qdev->small_buf_phy_addr_low + offset; + qdev->small_buf_release_cnt++; + + /* start of first buffer */ + lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); + lrg_buf_cb1 = &qdev->lrg_buf[qdev->lrg_buf_index]; + + qdev->lrg_buf_release_cnt++; + if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) + qdev->lrg_buf_index = 0; + skb1 = lrg_buf_cb1->skb; + curr_ial_ptr++; /* 64-bit pointers require two incs. */ + curr_ial_ptr++; + + /* start of second buffer */ + lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); + lrg_buf_cb2 = &qdev->lrg_buf[qdev->lrg_buf_index]; + skb2 = lrg_buf_cb2->skb; + qdev->lrg_buf_release_cnt++; + if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) + qdev->lrg_buf_index = 0; + + qdev->stats.rx_packets++; + qdev->stats.rx_bytes += length; + + /* + * Copy the ethhdr from first buffer to second. This + * is necessary for IP completions. + */ + if (*((u16 *) skb1->data) != 0xFFFF) + size = VLAN_ETH_HLEN; + else + size = ETH_HLEN; + + skb_put(skb2, length); /* Just the second buffer length here. */ + pci_unmap_single(qdev->pdev, + pci_unmap_addr(lrg_buf_cb2, mapaddr), + pci_unmap_len(lrg_buf_cb2, maplen), + PCI_DMA_FROMDEVICE); + prefetch(skb2->data); + + memcpy(skb_push(skb2, size), skb1->data + VLAN_ID_LEN, size); + skb2->dev = qdev->ndev; + skb2->ip_summed = CHECKSUM_NONE; + skb2->protocol = eth_type_trans(skb2, qdev->ndev); + + netif_receive_skb(skb2); + ndev->last_rx = jiffies; + lrg_buf_cb2->skb = NULL; + + ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1); + ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb2); +} + +static int ql_tx_rx_clean(struct ql3_adapter *qdev, + int *tx_cleaned, int *rx_cleaned, int work_to_do) +{ + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + struct net_rsp_iocb *net_rsp; + struct net_device *ndev = qdev->ndev; + unsigned long hw_flags; + + /* While there are entries in the completion queue. */ + while ((cpu_to_le32(*(qdev->prsp_producer_index)) != + qdev->rsp_consumer_index) && (*rx_cleaned < work_to_do)) { + + net_rsp = qdev->rsp_current; + switch (net_rsp->opcode) { + + case OPCODE_OB_MAC_IOCB_FN0: + case OPCODE_OB_MAC_IOCB_FN2: + ql_process_mac_tx_intr(qdev, (struct ob_mac_iocb_rsp *) + net_rsp); + (*tx_cleaned)++; + break; + + case OPCODE_IB_MAC_IOCB: + ql_process_mac_rx_intr(qdev, (struct ib_mac_iocb_rsp *) + net_rsp); + (*rx_cleaned)++; + break; + + case OPCODE_IB_IP_IOCB: + ql_process_macip_rx_intr(qdev, (struct ib_ip_iocb_rsp *) + net_rsp); + (*rx_cleaned)++; + break; + default: + { + u32 *tmp = (u32 *) net_rsp; + printk(KERN_ERR PFX + "%s: Hit default case, not " + "handled!\n" + " dropping the packet, opcode = " + "%x.\n", + ndev->name, net_rsp->opcode); + printk(KERN_ERR PFX + "0x%08lx 0x%08lx 0x%08lx 0x%08lx \n", + (unsigned long int)tmp[0], + (unsigned long int)tmp[1], + (unsigned long int)tmp[2], + (unsigned long int)tmp[3]); + } + } + + qdev->rsp_consumer_index++; + + if (qdev->rsp_consumer_index == NUM_RSP_Q_ENTRIES) { + qdev->rsp_consumer_index = 0; + qdev->rsp_current = qdev->rsp_q_virt_addr; + } else { + qdev->rsp_current++; + } + } + + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + + ql_update_lrg_bufq_prod_index(qdev); + + if (qdev->small_buf_release_cnt >= 16) { + while (qdev->small_buf_release_cnt >= 16) { + qdev->small_buf_q_producer_index++; + + if (qdev->small_buf_q_producer_index == + NUM_SBUFQ_ENTRIES) + qdev->small_buf_q_producer_index = 0; + qdev->small_buf_release_cnt -= 8; + } + + ql_write_common_reg(qdev, + (u32 *) & port_regs->CommonRegs. + rxSmallQProducerIndex, + qdev->small_buf_q_producer_index); + } + + ql_write_common_reg(qdev, + (u32 *) & port_regs->CommonRegs.rspQConsumerIndex, + qdev->rsp_consumer_index); + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + + if (unlikely(netif_queue_stopped(qdev->ndev))) { + if (netif_queue_stopped(qdev->ndev) && + (atomic_read(&qdev->tx_count) > (NUM_REQ_Q_ENTRIES / 4))) + netif_wake_queue(qdev->ndev); + } + + return *tx_cleaned + *rx_cleaned; +} + +static int ql_poll(struct net_device *ndev, int *budget) +{ + struct ql3_adapter *qdev = netdev_priv(ndev); + int work_to_do = min(*budget, ndev->quota); + int rx_cleaned = 0, tx_cleaned = 0; + + if (!netif_carrier_ok(ndev)) + goto quit_polling; + + ql_tx_rx_clean(qdev, &tx_cleaned, &rx_cleaned, work_to_do); + *budget -= rx_cleaned; + ndev->quota -= rx_cleaned; + + if ((!tx_cleaned && !rx_cleaned) || !netif_running(ndev)) { +quit_polling: + netif_rx_complete(ndev); + ql_enable_interrupts(qdev); + return 0; + } + return 1; +} + +static irqreturn_t ql3xxx_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + + struct net_device *ndev = dev_id; + struct ql3_adapter *qdev = netdev_priv(ndev); + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + u32 value; + int handled = 1; + u32 var; + + port_regs = qdev->mem_map_registers; + + value = + ql_read_common_reg_l(qdev, &port_regs->CommonRegs.ispControlStatus); + + if (value & (ISP_CONTROL_FE | ISP_CONTROL_RI)) { + spin_lock(&qdev->adapter_lock); + netif_stop_queue(qdev->ndev); + netif_carrier_off(qdev->ndev); + ql_disable_interrupts(qdev); + qdev->port_link_state = LS_DOWN; + set_bit(QL_RESET_ACTIVE,&qdev->flags) ; + + if (value & ISP_CONTROL_FE) { + /* + * Chip Fatal Error. + */ + var = + ql_read_page0_reg_l(qdev, + &port_regs->PortFatalErrStatus); + printk(KERN_WARNING PFX + "%s: Resetting chip. PortFatalErrStatus " + "register = 0x%x\n", ndev->name, var); + set_bit(QL_RESET_START,&qdev->flags) ; + } else { + /* + * Soft Reset Requested. + */ + set_bit(QL_RESET_PER_SCSI,&qdev->flags) ; + printk(KERN_ERR PFX + "%s: Another function issued a reset to the " + "chip. ISR value = %x.\n", ndev->name, value); + } + queue_work(qdev->workqueue, &qdev->reset_work); + spin_unlock(&qdev->adapter_lock); + } else if (value & ISP_IMR_DISABLE_CMPL_INT) { + ql_disable_interrupts(qdev); + if (likely(netif_rx_schedule_prep(ndev))) + __netif_rx_schedule(ndev); + else + ql_enable_interrupts(qdev); + } else { + return IRQ_NONE; + } + + return IRQ_RETVAL(handled); +} + +static int ql3xxx_send(struct sk_buff *skb, struct net_device *ndev) +{ + struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev); + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + struct ql_tx_buf_cb *tx_cb; + struct ob_mac_iocb_req *mac_iocb_ptr; + u64 map; + + if (unlikely(atomic_read(&qdev->tx_count) < 2)) { + if (!netif_queue_stopped(ndev)) + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; + } + tx_cb = &qdev->tx_buf[qdev->req_producer_index] ; + mac_iocb_ptr = tx_cb->queue_entry; + memset((void *)mac_iocb_ptr, 0, sizeof(struct ob_mac_iocb_req)); + mac_iocb_ptr->opcode = qdev->mac_ob_opcode; + mac_iocb_ptr->flags |= qdev->mb_bit_mask; + mac_iocb_ptr->transaction_id = qdev->req_producer_index; + mac_iocb_ptr->data_len = cpu_to_le16((u16) skb->len); + tx_cb->skb = skb; + map = pci_map_single(qdev->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + mac_iocb_ptr->buf_addr0_low = cpu_to_le32(LS_64BITS(map)); + mac_iocb_ptr->buf_addr0_high = cpu_to_le32(MS_64BITS(map)); + mac_iocb_ptr->buf_0_len = cpu_to_le32(skb->len | OB_MAC_IOCB_REQ_E); + pci_unmap_addr_set(tx_cb, mapaddr, map); + pci_unmap_len_set(tx_cb, maplen, skb->len); + atomic_dec(&qdev->tx_count); + + qdev->req_producer_index++; + if (qdev->req_producer_index == NUM_REQ_Q_ENTRIES) + qdev->req_producer_index = 0; + wmb(); + ql_write_common_reg_l(qdev, + (u32 *) & port_regs->CommonRegs.reqQProducerIndex, + qdev->req_producer_index); + + ndev->trans_start = jiffies; + if (netif_msg_tx_queued(qdev)) + printk(KERN_DEBUG PFX "%s: tx queued, slot %d, len %d\n", + ndev->name, qdev->req_producer_index, skb->len); + + return NETDEV_TX_OK; +} +static int ql_alloc_net_req_rsp_queues(struct ql3_adapter *qdev) +{ + qdev->req_q_size = + (u32) (NUM_REQ_Q_ENTRIES * sizeof(struct ob_mac_iocb_req)); + + qdev->req_q_virt_addr = + pci_alloc_consistent(qdev->pdev, + (size_t) qdev->req_q_size, + &qdev->req_q_phy_addr); + + if ((qdev->req_q_virt_addr == NULL) || + LS_64BITS(qdev->req_q_phy_addr) & (qdev->req_q_size - 1)) { + printk(KERN_ERR PFX "%s: reqQ failed.\n", + qdev->ndev->name); + return -ENOMEM; + } + + qdev->rsp_q_size = NUM_RSP_Q_ENTRIES * sizeof(struct net_rsp_iocb); + + qdev->rsp_q_virt_addr = + pci_alloc_consistent(qdev->pdev, + (size_t) qdev->rsp_q_size, + &qdev->rsp_q_phy_addr); + + if ((qdev->rsp_q_virt_addr == NULL) || + LS_64BITS(qdev->rsp_q_phy_addr) & (qdev->rsp_q_size - 1)) { + printk(KERN_ERR PFX + "%s: rspQ allocation failed\n", + qdev->ndev->name); + pci_free_consistent(qdev->pdev, (size_t) qdev->req_q_size, + qdev->req_q_virt_addr, + qdev->req_q_phy_addr); + return -ENOMEM; + } + + set_bit(QL_ALLOC_REQ_RSP_Q_DONE,&qdev->flags); + + return 0; +} + +static void ql_free_net_req_rsp_queues(struct ql3_adapter *qdev) +{ + if (!test_bit(QL_ALLOC_REQ_RSP_Q_DONE,&qdev->flags)) { + printk(KERN_INFO PFX + "%s: Already done.\n", qdev->ndev->name); + return; + } + + pci_free_consistent(qdev->pdev, + qdev->req_q_size, + qdev->req_q_virt_addr, qdev->req_q_phy_addr); + + qdev->req_q_virt_addr = NULL; + + pci_free_consistent(qdev->pdev, + qdev->rsp_q_size, + qdev->rsp_q_virt_addr, qdev->rsp_q_phy_addr); + + qdev->rsp_q_virt_addr = NULL; + + clear_bit(QL_ALLOC_REQ_RSP_Q_DONE,&qdev->flags); +} + +static int ql_alloc_buffer_queues(struct ql3_adapter *qdev) +{ + /* Create Large Buffer Queue */ + qdev->lrg_buf_q_size = + NUM_LBUFQ_ENTRIES * sizeof(struct lrg_buf_q_entry); + if (qdev->lrg_buf_q_size < PAGE_SIZE) + qdev->lrg_buf_q_alloc_size = PAGE_SIZE; + else + qdev->lrg_buf_q_alloc_size = qdev->lrg_buf_q_size * 2; + + qdev->lrg_buf_q_alloc_virt_addr = + pci_alloc_consistent(qdev->pdev, + qdev->lrg_buf_q_alloc_size, + &qdev->lrg_buf_q_alloc_phy_addr); + + if (qdev->lrg_buf_q_alloc_virt_addr == NULL) { + printk(KERN_ERR PFX + "%s: lBufQ failed\n", qdev->ndev->name); + return -ENOMEM; + } + qdev->lrg_buf_q_virt_addr = qdev->lrg_buf_q_alloc_virt_addr; + qdev->lrg_buf_q_phy_addr = qdev->lrg_buf_q_alloc_phy_addr; + + /* Create Small Buffer Queue */ + qdev->small_buf_q_size = + NUM_SBUFQ_ENTRIES * sizeof(struct lrg_buf_q_entry); + if (qdev->small_buf_q_size < PAGE_SIZE) + qdev->small_buf_q_alloc_size = PAGE_SIZE; + else + qdev->small_buf_q_alloc_size = qdev->small_buf_q_size * 2; + + qdev->small_buf_q_alloc_virt_addr = + pci_alloc_consistent(qdev->pdev, + qdev->small_buf_q_alloc_size, + &qdev->small_buf_q_alloc_phy_addr); + + if (qdev->small_buf_q_alloc_virt_addr == NULL) { + printk(KERN_ERR PFX + "%s: Small Buffer Queue allocation failed.\n", + qdev->ndev->name); + pci_free_consistent(qdev->pdev, qdev->lrg_buf_q_alloc_size, + qdev->lrg_buf_q_alloc_virt_addr, + qdev->lrg_buf_q_alloc_phy_addr); + return -ENOMEM; + } + + qdev->small_buf_q_virt_addr = qdev->small_buf_q_alloc_virt_addr; + qdev->small_buf_q_phy_addr = qdev->small_buf_q_alloc_phy_addr; + set_bit(QL_ALLOC_BUFQS_DONE,&qdev->flags); + return 0; +} + +static void ql_free_buffer_queues(struct ql3_adapter *qdev) +{ + if (!test_bit(QL_ALLOC_BUFQS_DONE,&qdev->flags)) { + printk(KERN_INFO PFX + "%s: Already done.\n", qdev->ndev->name); + return; + } + + pci_free_consistent(qdev->pdev, + qdev->lrg_buf_q_alloc_size, + qdev->lrg_buf_q_alloc_virt_addr, + qdev->lrg_buf_q_alloc_phy_addr); + + qdev->lrg_buf_q_virt_addr = NULL; + + pci_free_consistent(qdev->pdev, + qdev->small_buf_q_alloc_size, + qdev->small_buf_q_alloc_virt_addr, + qdev->small_buf_q_alloc_phy_addr); + + qdev->small_buf_q_virt_addr = NULL; + + clear_bit(QL_ALLOC_BUFQS_DONE,&qdev->flags); +} + +static int ql_alloc_small_buffers(struct ql3_adapter *qdev) +{ + int i; + struct bufq_addr_element *small_buf_q_entry; + + /* Currently we allocate on one of memory and use it for smallbuffers */ + qdev->small_buf_total_size = + (QL_ADDR_ELE_PER_BUFQ_ENTRY * NUM_SBUFQ_ENTRIES * + QL_SMALL_BUFFER_SIZE); + + qdev->small_buf_virt_addr = + pci_alloc_consistent(qdev->pdev, + qdev->small_buf_total_size, + &qdev->small_buf_phy_addr); + + if (qdev->small_buf_virt_addr == NULL) { + printk(KERN_ERR PFX + "%s: Failed to get small buffer memory.\n", + qdev->ndev->name); + return -ENOMEM; + } + + qdev->small_buf_phy_addr_low = LS_64BITS(qdev->small_buf_phy_addr); + qdev->small_buf_phy_addr_high = MS_64BITS(qdev->small_buf_phy_addr); + + small_buf_q_entry = qdev->small_buf_q_virt_addr; + + qdev->last_rsp_offset = qdev->small_buf_phy_addr_low; + + /* Initialize the small buffer queue. */ + for (i = 0; i < (QL_ADDR_ELE_PER_BUFQ_ENTRY * NUM_SBUFQ_ENTRIES); i++) { + small_buf_q_entry->addr_high = + cpu_to_le32(qdev->small_buf_phy_addr_high); + small_buf_q_entry->addr_low = + cpu_to_le32(qdev->small_buf_phy_addr_low + + (i * QL_SMALL_BUFFER_SIZE)); + small_buf_q_entry++; + } + qdev->small_buf_index = 0; + set_bit(QL_ALLOC_SMALL_BUF_DONE,&qdev->flags); + return 0; +} + +static void ql_free_small_buffers(struct ql3_adapter *qdev) +{ + if (!test_bit(QL_ALLOC_SMALL_BUF_DONE,&qdev->flags)) { + printk(KERN_INFO PFX + "%s: Already done.\n", qdev->ndev->name); + return; + } + if (qdev->small_buf_virt_addr != NULL) { + pci_free_consistent(qdev->pdev, + qdev->small_buf_total_size, + qdev->small_buf_virt_addr, + qdev->small_buf_phy_addr); + + qdev->small_buf_virt_addr = NULL; + } +} + +static void ql_free_large_buffers(struct ql3_adapter *qdev) +{ + int i = 0; + struct ql_rcv_buf_cb *lrg_buf_cb; + + for (i = 0; i < NUM_LARGE_BUFFERS; i++) { + lrg_buf_cb = &qdev->lrg_buf[i]; + if (lrg_buf_cb->skb) { + dev_kfree_skb(lrg_buf_cb->skb); + pci_unmap_single(qdev->pdev, + pci_unmap_addr(lrg_buf_cb, mapaddr), + pci_unmap_len(lrg_buf_cb, maplen), + PCI_DMA_FROMDEVICE); + memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb)); + } else { + break; + } + } +} + +static void ql_init_large_buffers(struct ql3_adapter *qdev) +{ + int i; + struct ql_rcv_buf_cb *lrg_buf_cb; + struct bufq_addr_element *buf_addr_ele = qdev->lrg_buf_q_virt_addr; + + for (i = 0; i < NUM_LARGE_BUFFERS; i++) { + lrg_buf_cb = &qdev->lrg_buf[i]; + buf_addr_ele->addr_high = lrg_buf_cb->buf_phy_addr_high; + buf_addr_ele->addr_low = lrg_buf_cb->buf_phy_addr_low; + buf_addr_ele++; + } + qdev->lrg_buf_index = 0; + qdev->lrg_buf_skb_check = 0; +} + +static int ql_alloc_large_buffers(struct ql3_adapter *qdev) +{ + int i; + struct ql_rcv_buf_cb *lrg_buf_cb; + struct sk_buff *skb; + u64 map; + + for (i = 0; i < NUM_LARGE_BUFFERS; i++) { + skb = dev_alloc_skb(qdev->lrg_buffer_len); + if (unlikely(!skb)) { + /* Better luck next round */ + printk(KERN_ERR PFX + "%s: large buff alloc failed, " + "for %d bytes at index %d.\n", + qdev->ndev->name, + qdev->lrg_buffer_len * 2, i); + ql_free_large_buffers(qdev); + return -ENOMEM; + } else { + + lrg_buf_cb = &qdev->lrg_buf[i]; + memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb)); + lrg_buf_cb->index = i; + lrg_buf_cb->skb = skb; + /* + * We save some space to copy the ethhdr from first + * buffer + */ + skb_reserve(skb, QL_HEADER_SPACE); + map = pci_map_single(qdev->pdev, + skb->data, + qdev->lrg_buffer_len - + QL_HEADER_SPACE, + PCI_DMA_FROMDEVICE); + pci_unmap_addr_set(lrg_buf_cb, mapaddr, map); + pci_unmap_len_set(lrg_buf_cb, maplen, + qdev->lrg_buffer_len - + QL_HEADER_SPACE); + lrg_buf_cb->buf_phy_addr_low = + cpu_to_le32(LS_64BITS(map)); + lrg_buf_cb->buf_phy_addr_high = + cpu_to_le32(MS_64BITS(map)); + } + } + return 0; +} + +static void ql_create_send_free_list(struct ql3_adapter *qdev) +{ + struct ql_tx_buf_cb *tx_cb; + int i; + struct ob_mac_iocb_req *req_q_curr = + qdev->req_q_virt_addr; + + /* Create free list of transmit buffers */ + for (i = 0; i < NUM_REQ_Q_ENTRIES; i++) { + tx_cb = &qdev->tx_buf[i]; + tx_cb->skb = NULL; + tx_cb->queue_entry = req_q_curr; + req_q_curr++; + } +} + +static int ql_alloc_mem_resources(struct ql3_adapter *qdev) +{ + if (qdev->ndev->mtu == NORMAL_MTU_SIZE) + qdev->lrg_buffer_len = NORMAL_MTU_SIZE; + else if (qdev->ndev->mtu == JUMBO_MTU_SIZE) { + qdev->lrg_buffer_len = JUMBO_MTU_SIZE; + } else { + printk(KERN_ERR PFX + "%s: Invalid mtu size. Only 1500 and 9000 are accepted.\n", + qdev->ndev->name); + return -ENOMEM; + } + qdev->lrg_buffer_len += VLAN_ETH_HLEN + VLAN_ID_LEN + QL_HEADER_SPACE; + qdev->max_frame_size = + (qdev->lrg_buffer_len - QL_HEADER_SPACE) + ETHERNET_CRC_SIZE; + + /* + * First allocate a page of shared memory and use it for shadow + * locations of Network Request Queue Consumer Address Register and + * Network Completion Queue Producer Index Register + */ + qdev->shadow_reg_virt_addr = + pci_alloc_consistent(qdev->pdev, + PAGE_SIZE, &qdev->shadow_reg_phy_addr); + + if (qdev->shadow_reg_virt_addr != NULL) { + qdev->preq_consumer_index = (u16 *) qdev->shadow_reg_virt_addr; + qdev->req_consumer_index_phy_addr_high = + MS_64BITS(qdev->shadow_reg_phy_addr); + qdev->req_consumer_index_phy_addr_low = + LS_64BITS(qdev->shadow_reg_phy_addr); + + qdev->prsp_producer_index = + (u32 *) (((u8 *) qdev->preq_consumer_index) + 8); + qdev->rsp_producer_index_phy_addr_high = + qdev->req_consumer_index_phy_addr_high; + qdev->rsp_producer_index_phy_addr_low = + qdev->req_consumer_index_phy_addr_low + 8; + } else { + printk(KERN_ERR PFX + "%s: shadowReg Alloc failed.\n", qdev->ndev->name); + return -ENOMEM; + } + + if (ql_alloc_net_req_rsp_queues(qdev) != 0) { + printk(KERN_ERR PFX + "%s: ql_alloc_net_req_rsp_queues failed.\n", + qdev->ndev->name); + goto err_req_rsp; + } + + if (ql_alloc_buffer_queues(qdev) != 0) { + printk(KERN_ERR PFX + "%s: ql_alloc_buffer_queues failed.\n", + qdev->ndev->name); + goto err_buffer_queues; + } + + if (ql_alloc_small_buffers(qdev) != 0) { + printk(KERN_ERR PFX + "%s: ql_alloc_small_buffers failed\n", qdev->ndev->name); + goto err_small_buffers; + } + + if (ql_alloc_large_buffers(qdev) != 0) { + printk(KERN_ERR PFX + "%s: ql_alloc_large_buffers failed\n", qdev->ndev->name); + goto err_small_buffers; + } + + /* Initialize the large buffer queue. */ + ql_init_large_buffers(qdev); + ql_create_send_free_list(qdev); + + qdev->rsp_current = qdev->rsp_q_virt_addr; + + return 0; + +err_small_buffers: + ql_free_buffer_queues(qdev); +err_buffer_queues: + ql_free_net_req_rsp_queues(qdev); +err_req_rsp: + pci_free_consistent(qdev->pdev, + PAGE_SIZE, + qdev->shadow_reg_virt_addr, + qdev->shadow_reg_phy_addr); + + return -ENOMEM; +} + +static void ql_free_mem_resources(struct ql3_adapter *qdev) +{ + ql_free_large_buffers(qdev); + ql_free_small_buffers(qdev); + ql_free_buffer_queues(qdev); + ql_free_net_req_rsp_queues(qdev); + if (qdev->shadow_reg_virt_addr != NULL) { + pci_free_consistent(qdev->pdev, + PAGE_SIZE, + qdev->shadow_reg_virt_addr, + qdev->shadow_reg_phy_addr); + qdev->shadow_reg_virt_addr = NULL; + } +} + +static int ql_init_misc_registers(struct ql3_adapter *qdev) +{ + struct ql3xxx_local_ram_registers *local_ram = + (struct ql3xxx_local_ram_registers *)qdev->mem_map_registers; + + if(ql_sem_spinlock(qdev, QL_DDR_RAM_SEM_MASK, + (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * + 2) << 4)) + return -1; + + ql_write_page2_reg(qdev, + &local_ram->bufletSize, qdev->nvram_data.bufletSize); + + ql_write_page2_reg(qdev, + &local_ram->maxBufletCount, + qdev->nvram_data.bufletCount); + + ql_write_page2_reg(qdev, + &local_ram->freeBufletThresholdLow, + (qdev->nvram_data.tcpWindowThreshold25 << 16) | + (qdev->nvram_data.tcpWindowThreshold0)); + + ql_write_page2_reg(qdev, + &local_ram->freeBufletThresholdHigh, + qdev->nvram_data.tcpWindowThreshold50); + + ql_write_page2_reg(qdev, + &local_ram->ipHashTableBase, + (qdev->nvram_data.ipHashTableBaseHi << 16) | + qdev->nvram_data.ipHashTableBaseLo); + ql_write_page2_reg(qdev, + &local_ram->ipHashTableCount, + qdev->nvram_data.ipHashTableSize); + ql_write_page2_reg(qdev, + &local_ram->tcpHashTableBase, + (qdev->nvram_data.tcpHashTableBaseHi << 16) | + qdev->nvram_data.tcpHashTableBaseLo); + ql_write_page2_reg(qdev, + &local_ram->tcpHashTableCount, + qdev->nvram_data.tcpHashTableSize); + ql_write_page2_reg(qdev, + &local_ram->ncbBase, + (qdev->nvram_data.ncbTableBaseHi << 16) | + qdev->nvram_data.ncbTableBaseLo); + ql_write_page2_reg(qdev, + &local_ram->maxNcbCount, + qdev->nvram_data.ncbTableSize); + ql_write_page2_reg(qdev, + &local_ram->drbBase, + (qdev->nvram_data.drbTableBaseHi << 16) | + qdev->nvram_data.drbTableBaseLo); + ql_write_page2_reg(qdev, + &local_ram->maxDrbCount, + qdev->nvram_data.drbTableSize); + ql_sem_unlock(qdev, QL_DDR_RAM_SEM_MASK); + return 0; +} + +static int ql_adapter_initialize(struct ql3_adapter *qdev) +{ + u32 value; + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + struct ql3xxx_host_memory_registers __iomem *hmem_regs = + (struct ql3xxx_host_memory_registers *)port_regs; + u32 delay = 10; + int status = 0; + + if(ql_mii_setup(qdev)) + return -1; + + /* Bring out PHY out of reset */ + ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg, + (ISP_SERIAL_PORT_IF_WE | + (ISP_SERIAL_PORT_IF_WE << 16))); + + qdev->port_link_state = LS_DOWN; + netif_carrier_off(qdev->ndev); + + /* V2 chip fix for ARS-39168. */ + ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg, + (ISP_SERIAL_PORT_IF_SDE | + (ISP_SERIAL_PORT_IF_SDE << 16))); + + /* Request Queue Registers */ + *((u32 *) (qdev->preq_consumer_index)) = 0; + atomic_set(&qdev->tx_count,NUM_REQ_Q_ENTRIES); + qdev->req_producer_index = 0; + + ql_write_page1_reg(qdev, + &hmem_regs->reqConsumerIndexAddrHigh, + qdev->req_consumer_index_phy_addr_high); + ql_write_page1_reg(qdev, + &hmem_regs->reqConsumerIndexAddrLow, + qdev->req_consumer_index_phy_addr_low); + + ql_write_page1_reg(qdev, + &hmem_regs->reqBaseAddrHigh, + MS_64BITS(qdev->req_q_phy_addr)); + ql_write_page1_reg(qdev, + &hmem_regs->reqBaseAddrLow, + LS_64BITS(qdev->req_q_phy_addr)); + ql_write_page1_reg(qdev, &hmem_regs->reqLength, NUM_REQ_Q_ENTRIES); + + /* Response Queue Registers */ + *((u16 *) (qdev->prsp_producer_index)) = 0; + qdev->rsp_consumer_index = 0; + qdev->rsp_current = qdev->rsp_q_virt_addr; + + ql_write_page1_reg(qdev, + &hmem_regs->rspProducerIndexAddrHigh, + qdev->rsp_producer_index_phy_addr_high); + + ql_write_page1_reg(qdev, + &hmem_regs->rspProducerIndexAddrLow, + qdev->rsp_producer_index_phy_addr_low); + + ql_write_page1_reg(qdev, + &hmem_regs->rspBaseAddrHigh, + MS_64BITS(qdev->rsp_q_phy_addr)); + + ql_write_page1_reg(qdev, + &hmem_regs->rspBaseAddrLow, + LS_64BITS(qdev->rsp_q_phy_addr)); + + ql_write_page1_reg(qdev, &hmem_regs->rspLength, NUM_RSP_Q_ENTRIES); + + /* Large Buffer Queue */ + ql_write_page1_reg(qdev, + &hmem_regs->rxLargeQBaseAddrHigh, + MS_64BITS(qdev->lrg_buf_q_phy_addr)); + + ql_write_page1_reg(qdev, + &hmem_regs->rxLargeQBaseAddrLow, + LS_64BITS(qdev->lrg_buf_q_phy_addr)); + + ql_write_page1_reg(qdev, &hmem_regs->rxLargeQLength, NUM_LBUFQ_ENTRIES); + + ql_write_page1_reg(qdev, + &hmem_regs->rxLargeBufferLength, + qdev->lrg_buffer_len); + + /* Small Buffer Queue */ + ql_write_page1_reg(qdev, + &hmem_regs->rxSmallQBaseAddrHigh, + MS_64BITS(qdev->small_buf_q_phy_addr)); + + ql_write_page1_reg(qdev, + &hmem_regs->rxSmallQBaseAddrLow, + LS_64BITS(qdev->small_buf_q_phy_addr)); + + ql_write_page1_reg(qdev, &hmem_regs->rxSmallQLength, NUM_SBUFQ_ENTRIES); + ql_write_page1_reg(qdev, + &hmem_regs->rxSmallBufferLength, + QL_SMALL_BUFFER_SIZE); + + qdev->small_buf_q_producer_index = NUM_SBUFQ_ENTRIES - 1; + qdev->small_buf_release_cnt = 8; + qdev->lrg_buf_q_producer_index = NUM_LBUFQ_ENTRIES - 1; + qdev->lrg_buf_release_cnt = 8; + qdev->lrg_buf_next_free = + (struct bufq_addr_element *)qdev->lrg_buf_q_virt_addr; + qdev->small_buf_index = 0; + qdev->lrg_buf_index = 0; + qdev->lrg_buf_free_count = 0; + qdev->lrg_buf_free_head = NULL; + qdev->lrg_buf_free_tail = NULL; + + ql_write_common_reg(qdev, + (u32 *) & port_regs->CommonRegs. + rxSmallQProducerIndex, + qdev->small_buf_q_producer_index); + ql_write_common_reg(qdev, + (u32 *) & port_regs->CommonRegs. + rxLargeQProducerIndex, + qdev->lrg_buf_q_producer_index); + + /* + * Find out if the chip has already been initialized. If it has, then + * we skip some of the initialization. + */ + clear_bit(QL_LINK_MASTER, &qdev->flags); + value = ql_read_page0_reg(qdev, &port_regs->portStatus); + if ((value & PORT_STATUS_IC) == 0) { + + /* Chip has not been configured yet, so let it rip. */ + if(ql_init_misc_registers(qdev)) { + status = -1; + goto out; + } + + if (qdev->mac_index) + ql_write_page0_reg(qdev, + &port_regs->mac1MaxFrameLengthReg, + qdev->max_frame_size); + else + ql_write_page0_reg(qdev, + &port_regs->mac0MaxFrameLengthReg, + qdev->max_frame_size); + + value = qdev->nvram_data.tcpMaxWindowSize; + ql_write_page0_reg(qdev, &port_regs->tcpMaxWindow, value); + + value = (0xFFFF << 16) | qdev->nvram_data.extHwConfig; + + if(ql_sem_spinlock(qdev, QL_FLASH_SEM_MASK, + (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) + * 2) << 13)) { + status = -1; + goto out; + } + ql_write_page0_reg(qdev, &port_regs->ExternalHWConfig, value); + ql_write_page0_reg(qdev, &port_regs->InternalChipConfig, + (((INTERNAL_CHIP_SD | INTERNAL_CHIP_WE) << + 16) | (INTERNAL_CHIP_SD | + INTERNAL_CHIP_WE))); + ql_sem_unlock(qdev, QL_FLASH_SEM_MASK); + } + + + if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, + (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * + 2) << 7)) { + status = -1; + goto out; + } + + ql_init_scan_mode(qdev); + ql_get_phy_owner(qdev); + + /* Load the MAC Configuration */ + + /* Program lower 32 bits of the MAC address */ + ql_write_page0_reg(qdev, &port_regs->macAddrIndirectPtrReg, + (MAC_ADDR_INDIRECT_PTR_REG_RP_MASK << 16)); + ql_write_page0_reg(qdev, &port_regs->macAddrDataReg, + ((qdev->ndev->dev_addr[2] << 24) + | (qdev->ndev->dev_addr[3] << 16) + | (qdev->ndev->dev_addr[4] << 8) + | qdev->ndev->dev_addr[5])); + + /* Program top 16 bits of the MAC address */ + ql_write_page0_reg(qdev, &port_regs->macAddrIndirectPtrReg, + ((MAC_ADDR_INDIRECT_PTR_REG_RP_MASK << 16) | 1)); + ql_write_page0_reg(qdev, &port_regs->macAddrDataReg, + ((qdev->ndev->dev_addr[0] << 8) + | qdev->ndev->dev_addr[1])); + + /* Enable Primary MAC */ + ql_write_page0_reg(qdev, &port_regs->macAddrIndirectPtrReg, + ((MAC_ADDR_INDIRECT_PTR_REG_PE << 16) | + MAC_ADDR_INDIRECT_PTR_REG_PE)); + + /* Clear Primary and Secondary IP addresses */ + ql_write_page0_reg(qdev, &port_regs->ipAddrIndexReg, + ((IP_ADDR_INDEX_REG_MASK << 16) | + (qdev->mac_index << 2))); + ql_write_page0_reg(qdev, &port_regs->ipAddrDataReg, 0); + + ql_write_page0_reg(qdev, &port_regs->ipAddrIndexReg, + ((IP_ADDR_INDEX_REG_MASK << 16) | + ((qdev->mac_index << 2) + 1))); + ql_write_page0_reg(qdev, &port_regs->ipAddrDataReg, 0); + + ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); + + /* Indicate Configuration Complete */ + ql_write_page0_reg(qdev, + &port_regs->portControl, + ((PORT_CONTROL_CC << 16) | PORT_CONTROL_CC)); + + do { + value = ql_read_page0_reg(qdev, &port_regs->portStatus); + if (value & PORT_STATUS_IC) + break; + msleep(500); + } while (--delay); + + if (delay == 0) { + printk(KERN_ERR PFX + "%s: Hw Initialization timeout.\n", qdev->ndev->name); + status = -1; + goto out; + } + + /* Enable Ethernet Function */ + value = + (PORT_CONTROL_EF | PORT_CONTROL_ET | PORT_CONTROL_EI | + PORT_CONTROL_HH); + ql_write_page0_reg(qdev, &port_regs->portControl, + ((value << 16) | value)); + +out: + return status; +} + +/* + * Caller holds hw_lock. + */ +static int ql_adapter_reset(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + int status = 0; + u16 value; + int max_wait_time; + + set_bit(QL_RESET_ACTIVE, &qdev->flags); + clear_bit(QL_RESET_DONE, &qdev->flags); + + /* + * Issue soft reset to chip. + */ + printk(KERN_DEBUG PFX + "%s: Issue soft reset to chip.\n", + qdev->ndev->name); + ql_write_common_reg(qdev, + (u32 *) & port_regs->CommonRegs.ispControlStatus, + ((ISP_CONTROL_SR << 16) | ISP_CONTROL_SR)); + + /* Wait 3 seconds for reset to complete. */ + printk(KERN_DEBUG PFX + "%s: Wait 10 milliseconds for reset to complete.\n", + qdev->ndev->name); + + /* Wait until the firmware tells us the Soft Reset is done */ + max_wait_time = 5; + do { + value = + ql_read_common_reg(qdev, + &port_regs->CommonRegs.ispControlStatus); + if ((value & ISP_CONTROL_SR) == 0) + break; + + ssleep(1); + } while ((--max_wait_time)); + + /* + * Also, make sure that the Network Reset Interrupt bit has been + * cleared after the soft reset has taken place. + */ + value = + ql_read_common_reg(qdev, &port_regs->CommonRegs.ispControlStatus); + if (value & ISP_CONTROL_RI) { + printk(KERN_DEBUG PFX + "ql_adapter_reset: clearing RI after reset.\n"); + ql_write_common_reg(qdev, + (u32 *) & port_regs->CommonRegs. + ispControlStatus, + ((ISP_CONTROL_RI << 16) | ISP_CONTROL_RI)); + } + + if (max_wait_time == 0) { + /* Issue Force Soft Reset */ + ql_write_common_reg(qdev, + (u32 *) & port_regs->CommonRegs. + ispControlStatus, + ((ISP_CONTROL_FSR << 16) | + ISP_CONTROL_FSR)); + /* + * Wait until the firmware tells us the Force Soft Reset is + * done + */ + max_wait_time = 5; + do { + value = + ql_read_common_reg(qdev, + &port_regs->CommonRegs. + ispControlStatus); + if ((value & ISP_CONTROL_FSR) == 0) { + break; + } + ssleep(1); + } while ((--max_wait_time)); + } + if (max_wait_time == 0) + status = 1; + + clear_bit(QL_RESET_ACTIVE, &qdev->flags); + set_bit(QL_RESET_DONE, &qdev->flags); + return status; +} + +static void ql_set_mac_info(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + u32 value, port_status; + u8 func_number; + + /* Get the function number */ + value = + ql_read_common_reg_l(qdev, &port_regs->CommonRegs.ispControlStatus); + func_number = (u8) ((value >> 4) & OPCODE_FUNC_ID_MASK); + port_status = ql_read_page0_reg(qdev, &port_regs->portStatus); + switch (value & ISP_CONTROL_FN_MASK) { + case ISP_CONTROL_FN0_NET: + qdev->mac_index = 0; + qdev->mac_ob_opcode = OUTBOUND_MAC_IOCB | func_number; + qdev->tcp_ob_opcode = OUTBOUND_TCP_IOCB | func_number; + qdev->update_ob_opcode = UPDATE_NCB_IOCB | func_number; + qdev->mb_bit_mask = FN0_MA_BITS_MASK; + qdev->PHYAddr = PORT0_PHY_ADDRESS; + if (port_status & PORT_STATUS_SM0) + set_bit(QL_LINK_OPTICAL,&qdev->flags); + else + clear_bit(QL_LINK_OPTICAL,&qdev->flags); + break; + + case ISP_CONTROL_FN1_NET: + qdev->mac_index = 1; + qdev->mac_ob_opcode = OUTBOUND_MAC_IOCB | func_number; + qdev->tcp_ob_opcode = OUTBOUND_TCP_IOCB | func_number; + qdev->update_ob_opcode = UPDATE_NCB_IOCB | func_number; + qdev->mb_bit_mask = FN1_MA_BITS_MASK; + qdev->PHYAddr = PORT1_PHY_ADDRESS; + if (port_status & PORT_STATUS_SM1) + set_bit(QL_LINK_OPTICAL,&qdev->flags); + else + clear_bit(QL_LINK_OPTICAL,&qdev->flags); + break; + + case ISP_CONTROL_FN0_SCSI: + case ISP_CONTROL_FN1_SCSI: + default: + printk(KERN_DEBUG PFX + "%s: Invalid function number, ispControlStatus = 0x%x\n", + qdev->ndev->name,value); + break; + } + qdev->numPorts = qdev->nvram_data.numPorts; +} + +static void ql_display_dev_info(struct net_device *ndev) +{ + struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev); + struct pci_dev *pdev = qdev->pdev; + + printk(KERN_INFO PFX + "\n%s Adapter %d RevisionID %d found on PCI slot %d.\n", + DRV_NAME, qdev->index, qdev->chip_rev_id, qdev->pci_slot); + printk(KERN_INFO PFX + "%s Interface.\n", + test_bit(QL_LINK_OPTICAL,&qdev->flags) ? "OPTICAL" : "COPPER"); + + /* + * Print PCI bus width/type. + */ + printk(KERN_INFO PFX + "Bus interface is %s %s.\n", + ((qdev->pci_width == 64) ? "64-bit" : "32-bit"), + ((qdev->pci_x) ? "PCI-X" : "PCI")); + + printk(KERN_INFO PFX + "mem IO base address adjusted = 0x%p\n", + qdev->mem_map_registers); + printk(KERN_INFO PFX "Interrupt number = %d\n", pdev->irq); + + if (netif_msg_probe(qdev)) + printk(KERN_INFO PFX + "%s: MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", + ndev->name, ndev->dev_addr[0], ndev->dev_addr[1], + ndev->dev_addr[2], ndev->dev_addr[3], ndev->dev_addr[4], + ndev->dev_addr[5]); +} + +static int ql_adapter_down(struct ql3_adapter *qdev, int do_reset) +{ + struct net_device *ndev = qdev->ndev; + int retval = 0; + + netif_stop_queue(ndev); + netif_carrier_off(ndev); + + clear_bit(QL_ADAPTER_UP,&qdev->flags); + clear_bit(QL_LINK_MASTER,&qdev->flags); + + ql_disable_interrupts(qdev); + + free_irq(qdev->pdev->irq, ndev); + + if (qdev->msi && test_bit(QL_MSI_ENABLED,&qdev->flags)) { + printk(KERN_INFO PFX + "%s: calling pci_disable_msi().\n", qdev->ndev->name); + clear_bit(QL_MSI_ENABLED,&qdev->flags); + pci_disable_msi(qdev->pdev); + } + + del_timer_sync(&qdev->adapter_timer); + + netif_poll_disable(ndev); + + if (do_reset) { + int soft_reset; + unsigned long hw_flags; + + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + if (ql_wait_for_drvr_lock(qdev)) { + if ((soft_reset = ql_adapter_reset(qdev))) { + printk(KERN_ERR PFX + "%s: ql_adapter_reset(%d) FAILED!\n", + ndev->name, qdev->index); + } + printk(KERN_ERR PFX + "%s: Releaseing driver lock via chip reset.\n",ndev->name); + } else { + printk(KERN_ERR PFX + "%s: Could not acquire driver lock to do " + "reset!\n", ndev->name); + retval = -1; + } + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + } + ql_free_mem_resources(qdev); + return retval; +} + +static int ql_adapter_up(struct ql3_adapter *qdev) +{ + struct net_device *ndev = qdev->ndev; + int err; + unsigned long irq_flags = SA_SAMPLE_RANDOM | SA_SHIRQ; + unsigned long hw_flags; + + if (ql_alloc_mem_resources(qdev)) { + printk(KERN_ERR PFX + "%s Unable to allocate buffers.\n", ndev->name); + return -ENOMEM; + } + + if (qdev->msi) { + if (pci_enable_msi(qdev->pdev)) { + printk(KERN_ERR PFX + "%s: User requested MSI, but MSI failed to " + "initialize. Continuing without MSI.\n", + qdev->ndev->name); + qdev->msi = 0; + } else { + printk(KERN_INFO PFX "%s: MSI Enabled...\n", qdev->ndev->name); + set_bit(QL_MSI_ENABLED,&qdev->flags); + irq_flags &= ~SA_SHIRQ; + } + } + + if ((err = request_irq(qdev->pdev->irq, + ql3xxx_isr, + irq_flags, ndev->name, ndev))) { + printk(KERN_ERR PFX + "%s: Failed to reserve interrupt %d already in use.\n", + ndev->name, qdev->pdev->irq); + goto err_irq; + } + + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + + if ((err = ql_wait_for_drvr_lock(qdev))) { + if ((err = ql_adapter_initialize(qdev))) { + printk(KERN_ERR PFX + "%s: Unable to initialize adapter.\n", + ndev->name); + goto err_init; + } + printk(KERN_ERR PFX + "%s: Releaseing driver lock.\n",ndev->name); + ql_sem_unlock(qdev, QL_DRVR_SEM_MASK); + } else { + printk(KERN_ERR PFX + "%s: Could not aquire driver lock.\n", + ndev->name); + goto err_lock; + } + + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + + set_bit(QL_ADAPTER_UP,&qdev->flags); + + mod_timer(&qdev->adapter_timer, jiffies + HZ * 1); + + netif_poll_enable(ndev); + ql_enable_interrupts(qdev); + return 0; + +err_init: + ql_sem_unlock(qdev, QL_DRVR_SEM_MASK); +err_lock: + free_irq(qdev->pdev->irq, ndev); +err_irq: + if (qdev->msi && test_bit(QL_MSI_ENABLED,&qdev->flags)) { + printk(KERN_INFO PFX + "%s: calling pci_disable_msi().\n", + qdev->ndev->name); + clear_bit(QL_MSI_ENABLED,&qdev->flags); + pci_disable_msi(qdev->pdev); + } + return err; +} + +static int ql_cycle_adapter(struct ql3_adapter *qdev, int reset) +{ + if( ql_adapter_down(qdev,reset) || ql_adapter_up(qdev)) { + printk(KERN_ERR PFX + "%s: Driver up/down cycle failed, " + "closing device\n",qdev->ndev->name); + dev_close(qdev->ndev); + return -1; + } + return 0; +} + +static int ql3xxx_close(struct net_device *ndev) +{ + struct ql3_adapter *qdev = netdev_priv(ndev); + + /* + * Wait for device to recover from a reset. + * (Rarely happens, but possible.) + */ + while (!test_bit(QL_ADAPTER_UP,&qdev->flags)) + msleep(50); + + ql_adapter_down(qdev,QL_DO_RESET); + return 0; +} + +static int ql3xxx_open(struct net_device *ndev) +{ + struct ql3_adapter *qdev = netdev_priv(ndev); + return (ql_adapter_up(qdev)); +} + +static struct net_device_stats *ql3xxx_get_stats(struct net_device *dev) +{ + struct ql3_adapter *qdev = (struct ql3_adapter *)dev->priv; + return &qdev->stats; +} + +static int ql3xxx_change_mtu(struct net_device *ndev, int new_mtu) +{ + struct ql3_adapter *qdev = netdev_priv(ndev); + printk(KERN_ERR PFX "%s: new mtu size = %d.\n", ndev->name, new_mtu); + if (new_mtu != NORMAL_MTU_SIZE && new_mtu != JUMBO_MTU_SIZE) { + printk(KERN_ERR PFX + "%s: mtu size of %d is not valid. Use exactly %d or " + "%d.\n", ndev->name, new_mtu, NORMAL_MTU_SIZE, + JUMBO_MTU_SIZE); + return -EINVAL; + } + + if (!netif_running(ndev)) { + ndev->mtu = new_mtu; + return 0; + } + + ndev->mtu = new_mtu; + return ql_cycle_adapter(qdev,QL_DO_RESET); +} + +static void ql3xxx_set_multicast_list(struct net_device *ndev) +{ + /* + * We are manually parsing the list in the net_device structure. + */ + return; +} + +static int ql3xxx_set_mac_address(struct net_device *ndev, void *p) +{ + struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev); + struct ql3xxx_port_registers __iomem *port_regs = + qdev->mem_map_registers; + struct sockaddr *addr = p; + unsigned long hw_flags; + + if (netif_running(ndev)) + return -EBUSY; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); + + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + /* Program lower 32 bits of the MAC address */ + ql_write_page0_reg(qdev, &port_regs->macAddrIndirectPtrReg, + (MAC_ADDR_INDIRECT_PTR_REG_RP_MASK << 16)); + ql_write_page0_reg(qdev, &port_regs->macAddrDataReg, + ((ndev->dev_addr[2] << 24) | (ndev-> + dev_addr[3] << 16) | + (ndev->dev_addr[4] << 8) | ndev->dev_addr[5])); + + /* Program top 16 bits of the MAC address */ + ql_write_page0_reg(qdev, &port_regs->macAddrIndirectPtrReg, + ((MAC_ADDR_INDIRECT_PTR_REG_RP_MASK << 16) | 1)); + ql_write_page0_reg(qdev, &port_regs->macAddrDataReg, + ((ndev->dev_addr[0] << 8) | ndev->dev_addr[1])); + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + + return 0; +} + +static void ql3xxx_tx_timeout(struct net_device *ndev) +{ + struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev); + + printk(KERN_ERR PFX "%s: Resetting...\n", ndev->name); + /* + * Stop the queues, we've got a problem. + */ + netif_stop_queue(ndev); + + /* + * Wake up the worker to process this event. + */ + queue_work(qdev->workqueue, &qdev->tx_timeout_work); +} + +static void ql_reset_work(struct ql3_adapter *qdev) +{ + struct net_device *ndev = qdev->ndev; + u32 value; + struct ql_tx_buf_cb *tx_cb; + int max_wait_time, i; + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + unsigned long hw_flags; + + if (test_bit((QL_RESET_PER_SCSI | QL_RESET_START),&qdev->flags)) { + clear_bit(QL_LINK_MASTER,&qdev->flags); + + /* + * Loop through the active list and return the skb. + */ + for (i = 0; i < NUM_REQ_Q_ENTRIES; i++) { + tx_cb = &qdev->tx_buf[i]; + if (tx_cb->skb) { + + printk(KERN_DEBUG PFX + "%s: Freeing lost SKB.\n", + qdev->ndev->name); + pci_unmap_single(qdev->pdev, + pci_unmap_addr(tx_cb, mapaddr), + pci_unmap_len(tx_cb, maplen), PCI_DMA_TODEVICE); + dev_kfree_skb(tx_cb->skb); + tx_cb->skb = NULL; + } + } + + printk(KERN_ERR PFX + "%s: Clearing NRI after reset.\n", qdev->ndev->name); + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + ql_write_common_reg(qdev, + &port_regs->CommonRegs. + ispControlStatus, + ((ISP_CONTROL_RI << 16) | ISP_CONTROL_RI)); + /* + * Wait the for Soft Reset to Complete. + */ + max_wait_time = 10; + do { + value = ql_read_common_reg(qdev, + &port_regs->CommonRegs. + + ispControlStatus); + if ((value & ISP_CONTROL_SR) == 0) { + printk(KERN_DEBUG PFX + "%s: reset completed.\n", + qdev->ndev->name); + break; + } + + if (value & ISP_CONTROL_RI) { + printk(KERN_DEBUG PFX + "%s: clearing NRI after reset.\n", + qdev->ndev->name); + ql_write_common_reg(qdev, + (u32 *) & + port_regs-> + CommonRegs. + ispControlStatus, + ((ISP_CONTROL_RI << + 16) | ISP_CONTROL_RI)); + } + + ssleep(1); + } while (--max_wait_time); + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + + if (value & ISP_CONTROL_SR) { + + /* + * Set the reset flags and clear the board again. + * Nothing else to do... + */ + printk(KERN_ERR PFX + "%s: Timed out waiting for reset to " + "complete.\n", ndev->name); + printk(KERN_ERR PFX + "%s: Do a reset.\n", ndev->name); + clear_bit(QL_RESET_PER_SCSI,&qdev->flags); + clear_bit(QL_RESET_START,&qdev->flags); + ql_cycle_adapter(qdev,QL_DO_RESET); + return; + } + + clear_bit(QL_RESET_ACTIVE,&qdev->flags); + clear_bit(QL_RESET_PER_SCSI,&qdev->flags); + clear_bit(QL_RESET_START,&qdev->flags); + ql_cycle_adapter(qdev,QL_NO_RESET); + } +} + +static void ql_tx_timeout_work(struct ql3_adapter *qdev) +{ + ql_cycle_adapter(qdev,QL_DO_RESET); +} + +static void ql_get_board_info(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + u32 value; + + value = ql_read_page0_reg_l(qdev, &port_regs->portStatus); + + qdev->chip_rev_id = ((value & PORT_STATUS_REV_ID_MASK) >> 12); + if (value & PORT_STATUS_64) + qdev->pci_width = 64; + else + qdev->pci_width = 32; + if (value & PORT_STATUS_X) + qdev->pci_x = 1; + else + qdev->pci_x = 0; + qdev->pci_slot = (u8) PCI_SLOT(qdev->pdev->devfn); +} + +static void ql3xxx_timer(unsigned long ptr) +{ + struct ql3_adapter *qdev = (struct ql3_adapter *)ptr; + + if (test_bit(QL_RESET_ACTIVE,&qdev->flags)) { + printk(KERN_DEBUG PFX + "%s: Reset in progress.\n", + qdev->ndev->name); + goto end; + } + + ql_link_state_machine(qdev); + + /* Restart timer on 2 second interval. */ +end: + mod_timer(&qdev->adapter_timer, jiffies + HZ * 1); +} + +static int __devinit ql3xxx_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_entry) +{ + struct net_device *ndev = NULL; + struct ql3_adapter *qdev = NULL; + static int cards_found = 0; + int pci_using_dac, err; + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR PFX "%s cannot enable PCI device\n", + pci_name(pdev)); + goto err_out; + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + printk(KERN_ERR PFX "%s cannot obtain PCI resources\n", + pci_name(pdev)); + goto err_out_disable_pdev; + } + + pci_set_master(pdev); + + if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { + pci_using_dac = 1; + err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); + } else if (!(err = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) { + pci_using_dac = 0; + err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + } + + if (err) { + printk(KERN_ERR PFX "%s no usable DMA configuration\n", + pci_name(pdev)); + goto err_out_free_regions; + } + + ndev = alloc_etherdev(sizeof(struct ql3_adapter)); + if (!ndev) + goto err_out_free_regions; + + SET_MODULE_OWNER(ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + + ndev->features = NETIF_F_LLTX; + if (pci_using_dac) + ndev->features |= NETIF_F_HIGHDMA; + + pci_set_drvdata(pdev, ndev); + + qdev = netdev_priv(ndev); + qdev->index = cards_found; + qdev->ndev = ndev; + qdev->pdev = pdev; + qdev->port_link_state = LS_DOWN; + if (msi) + qdev->msi = 1; + + qdev->msg_enable = netif_msg_init(debug, default_msg); + + qdev->mem_map_registers = + ioremap_nocache(pci_resource_start(pdev, 1), + pci_resource_len(qdev->pdev, 1)); + if (!qdev->mem_map_registers) { + printk(KERN_ERR PFX "%s: cannot map device registers\n", + pci_name(pdev)); + goto err_out_free_ndev; + } + + spin_lock_init(&qdev->adapter_lock); + spin_lock_init(&qdev->hw_lock); + + /* Set driver entry points */ + ndev->open = ql3xxx_open; + ndev->hard_start_xmit = ql3xxx_send; + ndev->stop = ql3xxx_close; + ndev->get_stats = ql3xxx_get_stats; + ndev->change_mtu = ql3xxx_change_mtu; + ndev->set_multicast_list = ql3xxx_set_multicast_list; + SET_ETHTOOL_OPS(ndev, &ql3xxx_ethtool_ops); + ndev->set_mac_address = ql3xxx_set_mac_address; + ndev->tx_timeout = ql3xxx_tx_timeout; + ndev->watchdog_timeo = 5 * HZ; + + ndev->poll = &ql_poll; + ndev->weight = 64; + + ndev->irq = pdev->irq; + + /* make sure the EEPROM is good */ + if (ql_get_nvram_params(qdev)) { + printk(KERN_ALERT PFX + "ql3xxx_probe: Adapter #%d, Invalid NVRAM parameters.\n", + qdev->index); + goto err_out_iounmap; + } + + ql_set_mac_info(qdev); + + /* Validate and set parameters */ + if (qdev->mac_index) { + memcpy(ndev->dev_addr, &qdev->nvram_data.funcCfg_fn2.macAddress, + ETH_ALEN); + } else { + memcpy(ndev->dev_addr, &qdev->nvram_data.funcCfg_fn0.macAddress, + ETH_ALEN); + } + memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len); + + ndev->tx_queue_len = NUM_REQ_Q_ENTRIES; + + /* Turn off support for multicasting */ + ndev->flags &= ~IFF_MULTICAST; + + /* Record PCI bus information. */ + ql_get_board_info(qdev); + + /* + * Set the Maximum Memory Read Byte Count value. We do this to handle + * jumbo frames. + */ + if (qdev->pci_x) { + pci_write_config_word(pdev, (int)0x4e, (u16) 0x0036); + } + + err = register_netdev(ndev); + if (err) { + printk(KERN_ERR PFX "%s: cannot register net device\n", + pci_name(pdev)); + goto err_out_iounmap; + } + + /* we're going to reset, so assume we have no link for now */ + + netif_carrier_off(ndev); + netif_stop_queue(ndev); + + qdev->workqueue = create_singlethread_workqueue(ndev->name); + INIT_WORK(&qdev->reset_work, (void (*)(void *))ql_reset_work, qdev); + INIT_WORK(&qdev->tx_timeout_work, + (void (*)(void *))ql_tx_timeout_work, qdev); + + init_timer(&qdev->adapter_timer); + qdev->adapter_timer.function = ql3xxx_timer; + qdev->adapter_timer.expires = jiffies + HZ * 2; /* two second delay */ + qdev->adapter_timer.data = (unsigned long)qdev; + + if(!cards_found) { + printk(KERN_ALERT PFX "%s\n", DRV_STRING); + printk(KERN_ALERT PFX "Driver name: %s, Version: %s.\n", + DRV_NAME, DRV_VERSION); + } + ql_display_dev_info(ndev); + + cards_found++; + return 0; + +err_out_iounmap: + iounmap(qdev->mem_map_registers); +err_out_free_ndev: + free_netdev(ndev); +err_out_free_regions: + pci_release_regions(pdev); +err_out_disable_pdev: + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +err_out: + return err; +} + +static void __devexit ql3xxx_remove(struct pci_dev *pdev) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + struct ql3_adapter *qdev = netdev_priv(ndev); + + unregister_netdev(ndev); + qdev = netdev_priv(ndev); + + ql_disable_interrupts(qdev); + + if (qdev->workqueue) { + cancel_delayed_work(&qdev->reset_work); + cancel_delayed_work(&qdev->tx_timeout_work); + destroy_workqueue(qdev->workqueue); + qdev->workqueue = NULL; + } + + iounmap((void *)qdev->mmap_virt_base); + pci_release_regions(pdev); + pci_set_drvdata(pdev, NULL); + free_netdev(ndev); +} + +static struct pci_driver ql3xxx_driver = { + + .name = DRV_NAME, + .id_table = ql3xxx_pci_tbl, + .probe = ql3xxx_probe, + .remove = __devexit_p(ql3xxx_remove), +}; + +static int __init ql3xxx_init_module(void) +{ + return pci_register_driver(&ql3xxx_driver); +} + +static void __exit ql3xxx_exit(void) +{ + pci_unregister_driver(&ql3xxx_driver); +} + +module_init(ql3xxx_init_module); +module_exit(ql3xxx_exit); diff --git a/drivers/net/qla3xxx.h b/drivers/net/qla3xxx.h new file mode 100644 index 00000000000..9492cee6b08 --- /dev/null +++ b/drivers/net/qla3xxx.h @@ -0,0 +1,1194 @@ +/* + * QLogic QLA3xxx NIC HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla3xxx for copyright and licensing details. + */ +#ifndef _QLA3XXX_H_ +#define _QLA3XXX_H_ + +/* + * IOCB Definitions... + */ +#pragma pack(1) + +#define OPCODE_OB_MAC_IOCB_FN0 0x01 +#define OPCODE_OB_MAC_IOCB_FN2 0x21 +#define OPCODE_OB_TCP_IOCB_FN0 0x03 +#define OPCODE_OB_TCP_IOCB_FN2 0x23 +#define OPCODE_UPDATE_NCB_IOCB_FN0 0x00 +#define OPCODE_UPDATE_NCB_IOCB_FN2 0x20 + +#define OPCODE_UPDATE_NCB_IOCB 0xF0 +#define OPCODE_IB_MAC_IOCB 0xF9 +#define OPCODE_IB_IP_IOCB 0xFA +#define OPCODE_IB_TCP_IOCB 0xFB +#define OPCODE_DUMP_PROTO_IOCB 0xFE +#define OPCODE_BUFFER_ALERT_IOCB 0xFB + +#define OPCODE_FUNC_ID_MASK 0x30 +#define OUTBOUND_MAC_IOCB 0x01 /* plus function bits */ +#define OUTBOUND_TCP_IOCB 0x03 /* plus function bits */ +#define UPDATE_NCB_IOCB 0x00 /* plus function bits */ + +#define FN0_MA_BITS_MASK 0x00 +#define FN1_MA_BITS_MASK 0x80 + +struct ob_mac_iocb_req { + u8 opcode; + u8 flags; +#define OB_MAC_IOCB_REQ_MA 0xC0 +#define OB_MAC_IOCB_REQ_F 0x20 +#define OB_MAC_IOCB_REQ_X 0x10 +#define OB_MAC_IOCB_REQ_D 0x02 +#define OB_MAC_IOCB_REQ_I 0x01 + __le16 reserved0; + + __le32 transaction_id; + __le16 data_len; + __le16 reserved1; + __le32 reserved2; + __le32 reserved3; + __le32 buf_addr0_low; + __le32 buf_addr0_high; + __le32 buf_0_len; + __le32 buf_addr1_low; + __le32 buf_addr1_high; + __le32 buf_1_len; + __le32 buf_addr2_low; + __le32 buf_addr2_high; + __le32 buf_2_len; + __le32 reserved4; + __le32 reserved5; +}; +/* + * The following constants define control bits for buffer + * length fields for all IOCB's. + */ +#define OB_MAC_IOCB_REQ_E 0x80000000 /* Last valid buffer in list. */ +#define OB_MAC_IOCB_REQ_C 0x40000000 /* points to an OAL. (continuation) */ +#define OB_MAC_IOCB_REQ_L 0x20000000 /* Auburn local address pointer. */ +#define OB_MAC_IOCB_REQ_R 0x10000000 /* 32-bit address pointer. */ + +struct ob_mac_iocb_rsp { + u8 opcode; + u8 flags; +#define OB_MAC_IOCB_RSP_P 0x08 +#define OB_MAC_IOCB_RSP_S 0x02 +#define OB_MAC_IOCB_RSP_I 0x01 + + __le16 reserved0; + __le32 transaction_id; + __le32 reserved1; + __le32 reserved2; +}; + +struct ib_mac_iocb_rsp { + u8 opcode; + u8 flags; +#define IB_MAC_IOCB_RSP_S 0x80 +#define IB_MAC_IOCB_RSP_H1 0x40 +#define IB_MAC_IOCB_RSP_H0 0x20 +#define IB_MAC_IOCB_RSP_B 0x10 +#define IB_MAC_IOCB_RSP_M 0x08 +#define IB_MAC_IOCB_RSP_MA 0x07 + + __le16 length; + __le32 reserved; + __le32 ial_low; + __le32 ial_high; + +}; + +struct ob_ip_iocb_req { + u8 opcode; + __le16 flags; +#define OB_IP_IOCB_REQ_O 0x100 +#define OB_IP_IOCB_REQ_H 0x008 +#define OB_IP_IOCB_REQ_U 0x004 +#define OB_IP_IOCB_REQ_D 0x002 +#define OB_IP_IOCB_REQ_I 0x001 + + u8 reserved0; + + __le32 transaction_id; + __le16 data_len; + __le16 reserved1; + __le32 hncb_ptr_low; + __le32 hncb_ptr_high; + __le32 buf_addr0_low; + __le32 buf_addr0_high; + __le32 buf_0_len; + __le32 buf_addr1_low; + __le32 buf_addr1_high; + __le32 buf_1_len; + __le32 buf_addr2_low; + __le32 buf_addr2_high; + __le32 buf_2_len; + __le32 reserved2; + __le32 reserved3; +}; + +/* defines for BufferLength fields above */ +#define OB_IP_IOCB_REQ_E 0x80000000 +#define OB_IP_IOCB_REQ_C 0x40000000 +#define OB_IP_IOCB_REQ_L 0x20000000 +#define OB_IP_IOCB_REQ_R 0x10000000 + +struct ob_ip_iocb_rsp { + u8 opcode; + u8 flags; +#define OB_MAC_IOCB_RSP_E 0x08 +#define OB_MAC_IOCB_RSP_L 0x04 +#define OB_MAC_IOCB_RSP_S 0x02 +#define OB_MAC_IOCB_RSP_I 0x01 + + __le16 reserved0; + __le32 transaction_id; + __le32 reserved1; + __le32 reserved2; +}; + +struct ob_tcp_iocb_req { + u8 opcode; + + u8 flags0; +#define OB_TCP_IOCB_REQ_P 0x80 +#define OB_TCP_IOCB_REQ_CI 0x20 +#define OB_TCP_IOCB_REQ_H 0x10 +#define OB_TCP_IOCB_REQ_LN 0x08 +#define OB_TCP_IOCB_REQ_K 0x04 +#define OB_TCP_IOCB_REQ_D 0x02 +#define OB_TCP_IOCB_REQ_I 0x01 + + u8 flags1; +#define OB_TCP_IOCB_REQ_OSM 0x40 +#define OB_TCP_IOCB_REQ_URG 0x20 +#define OB_TCP_IOCB_REQ_ACK 0x10 +#define OB_TCP_IOCB_REQ_PSH 0x08 +#define OB_TCP_IOCB_REQ_RST 0x04 +#define OB_TCP_IOCB_REQ_SYN 0x02 +#define OB_TCP_IOCB_REQ_FIN 0x01 + + u8 options_len; +#define OB_TCP_IOCB_REQ_OMASK 0xF0 +#define OB_TCP_IOCB_REQ_SHIFT 4 + + __le32 transaction_id; + __le32 data_len; + __le32 hncb_ptr_low; + __le32 hncb_ptr_high; + __le32 buf_addr0_low; + __le32 buf_addr0_high; + __le32 buf_0_len; + __le32 buf_addr1_low; + __le32 buf_addr1_high; + __le32 buf_1_len; + __le32 buf_addr2_low; + __le32 buf_addr2_high; + __le32 buf_2_len; + __le32 time_stamp; + __le32 reserved1; +}; + +struct ob_tcp_iocb_rsp { + u8 opcode; + + u8 flags0; +#define OB_TCP_IOCB_RSP_C 0x20 +#define OB_TCP_IOCB_RSP_H 0x10 +#define OB_TCP_IOCB_RSP_LN 0x08 +#define OB_TCP_IOCB_RSP_K 0x04 +#define OB_TCP_IOCB_RSP_D 0x02 +#define OB_TCP_IOCB_RSP_I 0x01 + + u8 flags1; +#define OB_TCP_IOCB_RSP_E 0x10 +#define OB_TCP_IOCB_RSP_W 0x08 +#define OB_TCP_IOCB_RSP_P 0x04 +#define OB_TCP_IOCB_RSP_T 0x02 +#define OB_TCP_IOCB_RSP_F 0x01 + + u8 state; +#define OB_TCP_IOCB_RSP_SMASK 0xF0 +#define OB_TCP_IOCB_RSP_SHIFT 4 + + __le32 transaction_id; + __le32 local_ncb_ptr; + __le32 reserved0; +}; + +struct ib_ip_iocb_rsp { + u8 opcode; + u8 flags; +#define IB_IP_IOCB_RSP_S 0x80 +#define IB_IP_IOCB_RSP_H1 0x40 +#define IB_IP_IOCB_RSP_H0 0x20 +#define IB_IP_IOCB_RSP_B 0x10 +#define IB_IP_IOCB_RSP_M 0x08 +#define IB_IP_IOCB_RSP_MA 0x07 + + __le16 length; + __le16 checksum; + __le16 reserved; +#define IB_IP_IOCB_RSP_R 0x01 + __le32 ial_low; + __le32 ial_high; +}; + +struct ib_tcp_iocb_rsp { + u8 opcode; + u8 flags; +#define IB_TCP_IOCB_RSP_P 0x80 +#define IB_TCP_IOCB_RSP_T 0x40 +#define IB_TCP_IOCB_RSP_D 0x20 +#define IB_TCP_IOCB_RSP_N 0x10 +#define IB_TCP_IOCB_RSP_IP 0x03 +#define IB_TCP_FLAG_MASK 0xf0 +#define IB_TCP_FLAG_IOCB_SYN 0x00 + +#define TCP_IB_RSP_FLAGS(x) (x->flags & ~IB_TCP_FLAG_MASK) + + __le16 length; + __le32 hncb_ref_num; + __le32 ial_low; + __le32 ial_high; +}; + +struct net_rsp_iocb { + u8 opcode; + u8 flags; + __le16 reserved0; + __le32 reserved[3]; +}; +#pragma pack() + +/* + * Register Definitions... + */ +#define PORT0_PHY_ADDRESS 0x1e00 +#define PORT1_PHY_ADDRESS 0x1f00 + +#define ETHERNET_CRC_SIZE 4 + +#define MII_SCAN_REGISTER 0x00000001 + +/* 32-bit ispControlStatus */ +enum { + ISP_CONTROL_NP_MASK = 0x0003, + ISP_CONTROL_NP_PCSR = 0x0000, + ISP_CONTROL_NP_HMCR = 0x0001, + ISP_CONTROL_NP_LRAMCR = 0x0002, + ISP_CONTROL_NP_PSR = 0x0003, + ISP_CONTROL_RI = 0x0008, + ISP_CONTROL_CI = 0x0010, + ISP_CONTROL_PI = 0x0020, + ISP_CONTROL_IN = 0x0040, + ISP_CONTROL_BE = 0x0080, + ISP_CONTROL_FN_MASK = 0x0700, + ISP_CONTROL_FN0_NET = 0x0400, + ISP_CONTROL_FN0_SCSI = 0x0500, + ISP_CONTROL_FN1_NET = 0x0600, + ISP_CONTROL_FN1_SCSI = 0x0700, + ISP_CONTROL_LINK_DN_0 = 0x0800, + ISP_CONTROL_LINK_DN_1 = 0x1000, + ISP_CONTROL_FSR = 0x2000, + ISP_CONTROL_FE = 0x4000, + ISP_CONTROL_SR = 0x8000, +}; + +/* 32-bit ispInterruptMaskReg */ +enum { + ISP_IMR_ENABLE_INT = 0x0004, + ISP_IMR_DISABLE_RESET_INT = 0x0008, + ISP_IMR_DISABLE_CMPL_INT = 0x0010, + ISP_IMR_DISABLE_PROC_INT = 0x0020, +}; + +/* 32-bit serialPortInterfaceReg */ +enum { + ISP_SERIAL_PORT_IF_CLK = 0x0001, + ISP_SERIAL_PORT_IF_CS = 0x0002, + ISP_SERIAL_PORT_IF_D0 = 0x0004, + ISP_SERIAL_PORT_IF_DI = 0x0008, + ISP_NVRAM_MASK = (0x000F << 16), + ISP_SERIAL_PORT_IF_WE = 0x0010, + ISP_SERIAL_PORT_IF_NVR_MASK = 0x001F, + ISP_SERIAL_PORT_IF_SCI = 0x0400, + ISP_SERIAL_PORT_IF_SC0 = 0x0800, + ISP_SERIAL_PORT_IF_SCE = 0x1000, + ISP_SERIAL_PORT_IF_SDI = 0x2000, + ISP_SERIAL_PORT_IF_SDO = 0x4000, + ISP_SERIAL_PORT_IF_SDE = 0x8000, + ISP_SERIAL_PORT_IF_I2C_MASK = 0xFC00, +}; + +/* semaphoreReg */ +enum { + QL_RESOURCE_MASK_BASE_CODE = 0x7, + QL_RESOURCE_BITS_BASE_CODE = 0x4, + QL_DRVR_SEM_BITS = (QL_RESOURCE_BITS_BASE_CODE << 1), + QL_DDR_RAM_SEM_BITS = (QL_RESOURCE_BITS_BASE_CODE << 4), + QL_PHY_GIO_SEM_BITS = (QL_RESOURCE_BITS_BASE_CODE << 7), + QL_NVRAM_SEM_BITS = (QL_RESOURCE_BITS_BASE_CODE << 10), + QL_FLASH_SEM_BITS = (QL_RESOURCE_BITS_BASE_CODE << 13), + QL_DRVR_SEM_MASK = (QL_RESOURCE_MASK_BASE_CODE << (1 + 16)), + QL_DDR_RAM_SEM_MASK = (QL_RESOURCE_MASK_BASE_CODE << (4 + 16)), + QL_PHY_GIO_SEM_MASK = (QL_RESOURCE_MASK_BASE_CODE << (7 + 16)), + QL_NVRAM_SEM_MASK = (QL_RESOURCE_MASK_BASE_CODE << (10 + 16)), + QL_FLASH_SEM_MASK = (QL_RESOURCE_MASK_BASE_CODE << (13 + 16)), +}; + + /* + * QL3XXX memory-mapped registers + * QL3XXX has 4 "pages" of registers, each page occupying + * 256 bytes. Each page has a "common" area at the start and then + * page-specific registers after that. + */ +struct ql3xxx_common_registers { + u32 MB0; /* Offset 0x00 */ + u32 MB1; /* Offset 0x04 */ + u32 MB2; /* Offset 0x08 */ + u32 MB3; /* Offset 0x0c */ + u32 MB4; /* Offset 0x10 */ + u32 MB5; /* Offset 0x14 */ + u32 MB6; /* Offset 0x18 */ + u32 MB7; /* Offset 0x1c */ + u32 flashBiosAddr; + u32 flashBiosData; + u32 ispControlStatus; + u32 ispInterruptMaskReg; + u32 serialPortInterfaceReg; + u32 semaphoreReg; + u32 reqQProducerIndex; + u32 rspQConsumerIndex; + + u32 rxLargeQProducerIndex; + u32 rxSmallQProducerIndex; + u32 arcMadiCommand; + u32 arcMadiData; +}; + +enum { + EXT_HW_CONFIG_SP_MASK = 0x0006, + EXT_HW_CONFIG_SP_NONE = 0x0000, + EXT_HW_CONFIG_SP_BYTE_PARITY = 0x0002, + EXT_HW_CONFIG_SP_ECC = 0x0004, + EXT_HW_CONFIG_SP_ECCx = 0x0006, + EXT_HW_CONFIG_SIZE_MASK = 0x0060, + EXT_HW_CONFIG_SIZE_128M = 0x0000, + EXT_HW_CONFIG_SIZE_256M = 0x0020, + EXT_HW_CONFIG_SIZE_512M = 0x0040, + EXT_HW_CONFIG_SIZE_INVALID = 0x0060, + EXT_HW_CONFIG_PD = 0x0080, + EXT_HW_CONFIG_FW = 0x0200, + EXT_HW_CONFIG_US = 0x0400, + EXT_HW_CONFIG_DCS_MASK = 0x1800, + EXT_HW_CONFIG_DCS_9MA = 0x0000, + EXT_HW_CONFIG_DCS_15MA = 0x0800, + EXT_HW_CONFIG_DCS_18MA = 0x1000, + EXT_HW_CONFIG_DCS_24MA = 0x1800, + EXT_HW_CONFIG_DDS_MASK = 0x6000, + EXT_HW_CONFIG_DDS_9MA = 0x0000, + EXT_HW_CONFIG_DDS_15MA = 0x2000, + EXT_HW_CONFIG_DDS_18MA = 0x4000, + EXT_HW_CONFIG_DDS_24MA = 0x6000, +}; + +/* InternalChipConfig */ +enum { + INTERNAL_CHIP_DM = 0x0001, + INTERNAL_CHIP_SD = 0x0002, + INTERNAL_CHIP_RAP_MASK = 0x000C, + INTERNAL_CHIP_RAP_RR = 0x0000, + INTERNAL_CHIP_RAP_NRM = 0x0004, + INTERNAL_CHIP_RAP_ERM = 0x0008, + INTERNAL_CHIP_RAP_ERMx = 0x000C, + INTERNAL_CHIP_WE = 0x0010, + INTERNAL_CHIP_EF = 0x0020, + INTERNAL_CHIP_FR = 0x0040, + INTERNAL_CHIP_FW = 0x0080, + INTERNAL_CHIP_FI = 0x0100, + INTERNAL_CHIP_FT = 0x0200, +}; + +/* portControl */ +enum { + PORT_CONTROL_DS = 0x0001, + PORT_CONTROL_HH = 0x0002, + PORT_CONTROL_EI = 0x0004, + PORT_CONTROL_ET = 0x0008, + PORT_CONTROL_EF = 0x0010, + PORT_CONTROL_DRM = 0x0020, + PORT_CONTROL_RLB = 0x0040, + PORT_CONTROL_RCB = 0x0080, + PORT_CONTROL_MAC = 0x0100, + PORT_CONTROL_IPV = 0x0200, + PORT_CONTROL_IFP = 0x0400, + PORT_CONTROL_ITP = 0x0800, + PORT_CONTROL_FI = 0x1000, + PORT_CONTROL_DFP = 0x2000, + PORT_CONTROL_OI = 0x4000, + PORT_CONTROL_CC = 0x8000, +}; + +/* portStatus */ +enum { + PORT_STATUS_SM0 = 0x0001, + PORT_STATUS_SM1 = 0x0002, + PORT_STATUS_X = 0x0008, + PORT_STATUS_DL = 0x0080, + PORT_STATUS_IC = 0x0200, + PORT_STATUS_MRC = 0x0400, + PORT_STATUS_NL = 0x0800, + PORT_STATUS_REV_ID_MASK = 0x7000, + PORT_STATUS_REV_ID_1 = 0x1000, + PORT_STATUS_REV_ID_2 = 0x2000, + PORT_STATUS_REV_ID_3 = 0x3000, + PORT_STATUS_64 = 0x8000, + PORT_STATUS_UP0 = 0x10000, + PORT_STATUS_AC0 = 0x20000, + PORT_STATUS_AE0 = 0x40000, + PORT_STATUS_UP1 = 0x100000, + PORT_STATUS_AC1 = 0x200000, + PORT_STATUS_AE1 = 0x400000, + PORT_STATUS_F0_ENABLED = 0x1000000, + PORT_STATUS_F1_ENABLED = 0x2000000, + PORT_STATUS_F2_ENABLED = 0x4000000, + PORT_STATUS_F3_ENABLED = 0x8000000, +}; + +/* macMIIMgmtControlReg */ +enum { + MAC_ADDR_INDIRECT_PTR_REG_RP_MASK = 0x0003, + MAC_ADDR_INDIRECT_PTR_REG_RP_PRI_LWR = 0x0000, + MAC_ADDR_INDIRECT_PTR_REG_RP_PRI_UPR = 0x0001, + MAC_ADDR_INDIRECT_PTR_REG_RP_SEC_LWR = 0x0002, + MAC_ADDR_INDIRECT_PTR_REG_RP_SEC_UPR = 0x0003, + MAC_ADDR_INDIRECT_PTR_REG_PR = 0x0008, + MAC_ADDR_INDIRECT_PTR_REG_SS = 0x0010, + MAC_ADDR_INDIRECT_PTR_REG_SE = 0x0020, + MAC_ADDR_INDIRECT_PTR_REG_SP = 0x0040, + MAC_ADDR_INDIRECT_PTR_REG_PE = 0x0080, +}; + +/* macMIIMgmtControlReg */ +enum { + MAC_MII_CONTROL_RC = 0x0001, + MAC_MII_CONTROL_SC = 0x0002, + MAC_MII_CONTROL_AS = 0x0004, + MAC_MII_CONTROL_NP = 0x0008, + MAC_MII_CONTROL_CLK_SEL_MASK = 0x0070, + MAC_MII_CONTROL_CLK_SEL_DIV2 = 0x0000, + MAC_MII_CONTROL_CLK_SEL_DIV4 = 0x0010, + MAC_MII_CONTROL_CLK_SEL_DIV6 = 0x0020, + MAC_MII_CONTROL_CLK_SEL_DIV8 = 0x0030, + MAC_MII_CONTROL_CLK_SEL_DIV10 = 0x0040, + MAC_MII_CONTROL_CLK_SEL_DIV14 = 0x0050, + MAC_MII_CONTROL_CLK_SEL_DIV20 = 0x0060, + MAC_MII_CONTROL_CLK_SEL_DIV28 = 0x0070, + MAC_MII_CONTROL_RM = 0x8000, +}; + +/* macMIIStatusReg */ +enum { + MAC_MII_STATUS_BSY = 0x0001, + MAC_MII_STATUS_SC = 0x0002, + MAC_MII_STATUS_NV = 0x0004, +}; + +enum { + MAC_CONFIG_REG_PE = 0x0001, + MAC_CONFIG_REG_TF = 0x0002, + MAC_CONFIG_REG_RF = 0x0004, + MAC_CONFIG_REG_FD = 0x0008, + MAC_CONFIG_REG_GM = 0x0010, + MAC_CONFIG_REG_LB = 0x0020, + MAC_CONFIG_REG_SR = 0x8000, +}; + +enum { + MAC_HALF_DUPLEX_REG_ED = 0x10000, + MAC_HALF_DUPLEX_REG_NB = 0x20000, + MAC_HALF_DUPLEX_REG_BNB = 0x40000, + MAC_HALF_DUPLEX_REG_ALT = 0x80000, +}; + +enum { + IP_ADDR_INDEX_REG_MASK = 0x000f, + IP_ADDR_INDEX_REG_FUNC_0_PRI = 0x0000, + IP_ADDR_INDEX_REG_FUNC_0_SEC = 0x0001, + IP_ADDR_INDEX_REG_FUNC_1_PRI = 0x0002, + IP_ADDR_INDEX_REG_FUNC_1_SEC = 0x0003, + IP_ADDR_INDEX_REG_FUNC_2_PRI = 0x0004, + IP_ADDR_INDEX_REG_FUNC_2_SEC = 0x0005, + IP_ADDR_INDEX_REG_FUNC_3_PRI = 0x0006, + IP_ADDR_INDEX_REG_FUNC_3_SEC = 0x0007, +}; + +enum { + PROBE_MUX_ADDR_REG_MUX_SEL_MASK = 0x003f, + PROBE_MUX_ADDR_REG_SYSCLK = 0x0000, + PROBE_MUX_ADDR_REG_PCICLK = 0x0040, + PROBE_MUX_ADDR_REG_NRXCLK = 0x0080, + PROBE_MUX_ADDR_REG_CPUCLK = 0x00C0, + PROBE_MUX_ADDR_REG_MODULE_SEL_MASK = 0x3f00, + PROBE_MUX_ADDR_REG_UP = 0x4000, + PROBE_MUX_ADDR_REG_RE = 0x8000, +}; + +enum { + STATISTICS_INDEX_REG_MASK = 0x01ff, + STATISTICS_INDEX_REG_MAC0_TX_FRAME = 0x0000, + STATISTICS_INDEX_REG_MAC0_TX_BYTES = 0x0001, + STATISTICS_INDEX_REG_MAC0_TX_STAT1 = 0x0002, + STATISTICS_INDEX_REG_MAC0_TX_STAT2 = 0x0003, + STATISTICS_INDEX_REG_MAC0_TX_STAT3 = 0x0004, + STATISTICS_INDEX_REG_MAC0_TX_STAT4 = 0x0005, + STATISTICS_INDEX_REG_MAC0_TX_STAT5 = 0x0006, + STATISTICS_INDEX_REG_MAC0_RX_FRAME = 0x0007, + STATISTICS_INDEX_REG_MAC0_RX_BYTES = 0x0008, + STATISTICS_INDEX_REG_MAC0_RX_STAT1 = 0x0009, + STATISTICS_INDEX_REG_MAC0_RX_STAT2 = 0x000a, + STATISTICS_INDEX_REG_MAC0_RX_STAT3 = 0x000b, + STATISTICS_INDEX_REG_MAC0_RX_ERR_CRC = 0x000c, + STATISTICS_INDEX_REG_MAC0_RX_ERR_ENC = 0x000d, + STATISTICS_INDEX_REG_MAC0_RX_ERR_LEN = 0x000e, + STATISTICS_INDEX_REG_MAC0_RX_STAT4 = 0x000f, + STATISTICS_INDEX_REG_MAC1_TX_FRAME = 0x0010, + STATISTICS_INDEX_REG_MAC1_TX_BYTES = 0x0011, + STATISTICS_INDEX_REG_MAC1_TX_STAT1 = 0x0012, + STATISTICS_INDEX_REG_MAC1_TX_STAT2 = 0x0013, + STATISTICS_INDEX_REG_MAC1_TX_STAT3 = 0x0014, + STATISTICS_INDEX_REG_MAC1_TX_STAT4 = 0x0015, + STATISTICS_INDEX_REG_MAC1_TX_STAT5 = 0x0016, + STATISTICS_INDEX_REG_MAC1_RX_FRAME = 0x0017, + STATISTICS_INDEX_REG_MAC1_RX_BYTES = 0x0018, + STATISTICS_INDEX_REG_MAC1_RX_STAT1 = 0x0019, + STATISTICS_INDEX_REG_MAC1_RX_STAT2 = 0x001a, + STATISTICS_INDEX_REG_MAC1_RX_STAT3 = 0x001b, + STATISTICS_INDEX_REG_MAC1_RX_ERR_CRC = 0x001c, + STATISTICS_INDEX_REG_MAC1_RX_ERR_ENC = 0x001d, + STATISTICS_INDEX_REG_MAC1_RX_ERR_LEN = 0x001e, + STATISTICS_INDEX_REG_MAC1_RX_STAT4 = 0x001f, + STATISTICS_INDEX_REG_IP_TX_PKTS = 0x0020, + STATISTICS_INDEX_REG_IP_TX_BYTES = 0x0021, + STATISTICS_INDEX_REG_IP_TX_FRAG = 0x0022, + STATISTICS_INDEX_REG_IP_RX_PKTS = 0x0023, + STATISTICS_INDEX_REG_IP_RX_BYTES = 0x0024, + STATISTICS_INDEX_REG_IP_RX_FRAG = 0x0025, + STATISTICS_INDEX_REG_IP_DGRM_REASSEMBLY = 0x0026, + STATISTICS_INDEX_REG_IP_V6_RX_PKTS = 0x0027, + STATISTICS_INDEX_REG_IP_RX_PKTERR = 0x0028, + STATISTICS_INDEX_REG_IP_REASSEMBLY_ERR = 0x0029, + STATISTICS_INDEX_REG_TCP_TX_SEG = 0x0030, + STATISTICS_INDEX_REG_TCP_TX_BYTES = 0x0031, + STATISTICS_INDEX_REG_TCP_RX_SEG = 0x0032, + STATISTICS_INDEX_REG_TCP_RX_BYTES = 0x0033, + STATISTICS_INDEX_REG_TCP_TIMER_EXP = 0x0034, + STATISTICS_INDEX_REG_TCP_RX_ACK = 0x0035, + STATISTICS_INDEX_REG_TCP_TX_ACK = 0x0036, + STATISTICS_INDEX_REG_TCP_RX_ERR = 0x0037, + STATISTICS_INDEX_REG_TCP_RX_WIN_PROBE = 0x0038, + STATISTICS_INDEX_REG_TCP_ECC_ERR_CORR = 0x003f, +}; + +enum { + PORT_FATAL_ERROR_STATUS_OFB_RE_MAC0 = 0x00000001, + PORT_FATAL_ERROR_STATUS_OFB_RE_MAC1 = 0x00000002, + PORT_FATAL_ERROR_STATUS_OFB_WE = 0x00000004, + PORT_FATAL_ERROR_STATUS_IFB_RE = 0x00000008, + PORT_FATAL_ERROR_STATUS_IFB_WE_MAC0 = 0x00000010, + PORT_FATAL_ERROR_STATUS_IFB_WE_MAC1 = 0x00000020, + PORT_FATAL_ERROR_STATUS_ODE_RE = 0x00000040, + PORT_FATAL_ERROR_STATUS_ODE_WE = 0x00000080, + PORT_FATAL_ERROR_STATUS_IDE_RE = 0x00000100, + PORT_FATAL_ERROR_STATUS_IDE_WE = 0x00000200, + PORT_FATAL_ERROR_STATUS_SDE_RE = 0x00000400, + PORT_FATAL_ERROR_STATUS_SDE_WE = 0x00000800, + PORT_FATAL_ERROR_STATUS_BLE = 0x00001000, + PORT_FATAL_ERROR_STATUS_SPE = 0x00002000, + PORT_FATAL_ERROR_STATUS_EP0 = 0x00004000, + PORT_FATAL_ERROR_STATUS_EP1 = 0x00008000, + PORT_FATAL_ERROR_STATUS_ICE = 0x00010000, + PORT_FATAL_ERROR_STATUS_ILE = 0x00020000, + PORT_FATAL_ERROR_STATUS_OPE = 0x00040000, + PORT_FATAL_ERROR_STATUS_TA = 0x00080000, + PORT_FATAL_ERROR_STATUS_MA = 0x00100000, + PORT_FATAL_ERROR_STATUS_SCE = 0x00200000, + PORT_FATAL_ERROR_STATUS_RPE = 0x00400000, + PORT_FATAL_ERROR_STATUS_MPE = 0x00800000, + PORT_FATAL_ERROR_STATUS_OCE = 0x01000000, +}; + +/* + * port control and status page - page 0 + */ + +struct ql3xxx_port_registers { + struct ql3xxx_common_registers CommonRegs; + + u32 ExternalHWConfig; + u32 InternalChipConfig; + u32 portControl; + u32 portStatus; + u32 macAddrIndirectPtrReg; + u32 macAddrDataReg; + u32 macMIIMgmtControlReg; + u32 macMIIMgmtAddrReg; + u32 macMIIMgmtDataReg; + u32 macMIIStatusReg; + u32 mac0ConfigReg; + u32 mac0IpgIfgReg; + u32 mac0HalfDuplexReg; + u32 mac0MaxFrameLengthReg; + u32 mac0PauseThresholdReg; + u32 mac1ConfigReg; + u32 mac1IpgIfgReg; + u32 mac1HalfDuplexReg; + u32 mac1MaxFrameLengthReg; + u32 mac1PauseThresholdReg; + u32 ipAddrIndexReg; + u32 ipAddrDataReg; + u32 ipReassemblyTimeout; + u32 tcpMaxWindow; + u32 currentTcpTimestamp[2]; + u32 internalRamRWAddrReg; + u32 internalRamWDataReg; + u32 reclaimedBufferAddrRegLow; + u32 reclaimedBufferAddrRegHigh; + u32 reserved[2]; + u32 fpgaRevID; + u32 localRamAddr; + u32 localRamDataAutoIncr; + u32 localRamDataNonIncr; + u32 gpOutput; + u32 gpInput; + u32 probeMuxAddr; + u32 probeMuxData; + u32 statisticsIndexReg; + u32 statisticsReadDataRegAutoIncr; + u32 statisticsReadDataRegNoIncr; + u32 PortFatalErrStatus; +}; + +/* + * port host memory config page - page 1 + */ +struct ql3xxx_host_memory_registers { + struct ql3xxx_common_registers CommonRegs; + + u32 reserved[12]; + + /* Network Request Queue */ + u32 reqConsumerIndex; + u32 reqConsumerIndexAddrLow; + u32 reqConsumerIndexAddrHigh; + u32 reqBaseAddrLow; + u32 reqBaseAddrHigh; + u32 reqLength; + + /* Network Completion Queue */ + u32 rspProducerIndex; + u32 rspProducerIndexAddrLow; + u32 rspProducerIndexAddrHigh; + u32 rspBaseAddrLow; + u32 rspBaseAddrHigh; + u32 rspLength; + + /* RX Large Buffer Queue */ + u32 rxLargeQConsumerIndex; + u32 rxLargeQBaseAddrLow; + u32 rxLargeQBaseAddrHigh; + u32 rxLargeQLength; + u32 rxLargeBufferLength; + + /* RX Small Buffer Queue */ + u32 rxSmallQConsumerIndex; + u32 rxSmallQBaseAddrLow; + u32 rxSmallQBaseAddrHigh; + u32 rxSmallQLength; + u32 rxSmallBufferLength; + +}; + +/* + * port local RAM page - page 2 + */ +struct ql3xxx_local_ram_registers { + struct ql3xxx_common_registers CommonRegs; + u32 bufletSize; + u32 maxBufletCount; + u32 currentBufletCount; + u32 reserved; + u32 freeBufletThresholdLow; + u32 freeBufletThresholdHigh; + u32 ipHashTableBase; + u32 ipHashTableCount; + u32 tcpHashTableBase; + u32 tcpHashTableCount; + u32 ncbBase; + u32 maxNcbCount; + u32 currentNcbCount; + u32 drbBase; + u32 maxDrbCount; + u32 currentDrbCount; +}; + +/* + * definitions for Semaphore bits in Semaphore/Serial NVRAM interface register + */ + +#define LS_64BITS(x) (u32)(0xffffffff & ((u64)x)) +#define MS_64BITS(x) (u32)(0xffffffff & (((u64)x)>>16>>16) ) + +/* + * I/O register + */ + +enum { + CONTROL_REG = 0, + STATUS_REG = 1, + PHY_STAT_LINK_UP = 0x0004, + PHY_CTRL_LOOPBACK = 0x4000, + + PETBI_CONTROL_REG = 0x00, + PETBI_CTRL_SOFT_RESET = 0x8000, + PETBI_CTRL_AUTO_NEG = 0x1000, + PETBI_CTRL_RESTART_NEG = 0x0200, + PETBI_CTRL_FULL_DUPLEX = 0x0100, + PETBI_CTRL_SPEED_1000 = 0x0040, + + PETBI_STATUS_REG = 0x01, + PETBI_STAT_NEG_DONE = 0x0020, + PETBI_STAT_LINK_UP = 0x0004, + + PETBI_NEG_ADVER = 0x04, + PETBI_NEG_PAUSE = 0x0080, + PETBI_NEG_PAUSE_MASK = 0x0180, + PETBI_NEG_DUPLEX = 0x0020, + PETBI_NEG_DUPLEX_MASK = 0x0060, + + PETBI_NEG_PARTNER = 0x05, + PETBI_NEG_ERROR_MASK = 0x3000, + + PETBI_EXPANSION_REG = 0x06, + PETBI_EXP_PAGE_RX = 0x0002, + + PETBI_TBI_CTRL = 0x11, + PETBI_TBI_RESET = 0x8000, + PETBI_TBI_AUTO_SENSE = 0x0100, + PETBI_TBI_SERDES_MODE = 0x0010, + PETBI_TBI_SERDES_WRAP = 0x0002, + + AUX_CONTROL_STATUS = 0x1c, + PHY_AUX_NEG_DONE = 0x8000, + PHY_NEG_PARTNER = 5, + PHY_AUX_DUPLEX_STAT = 0x0020, + PHY_AUX_SPEED_STAT = 0x0018, + PHY_AUX_NO_HW_STRAP = 0x0004, + PHY_AUX_RESET_STICK = 0x0002, + PHY_NEG_PAUSE = 0x0400, + PHY_CTRL_SOFT_RESET = 0x8000, + PHY_NEG_ADVER = 4, + PHY_NEG_ADV_SPEED = 0x01e0, + PHY_CTRL_RESTART_NEG = 0x0200, +}; +enum { +/* AM29LV Flash definitions */ + FM93C56A_START = 0x1, +/* Commands */ + FM93C56A_READ = 0x2, + FM93C56A_WEN = 0x0, + FM93C56A_WRITE = 0x1, + FM93C56A_WRITE_ALL = 0x0, + FM93C56A_WDS = 0x0, + FM93C56A_ERASE = 0x3, + FM93C56A_ERASE_ALL = 0x0, +/* Command Extentions */ + FM93C56A_WEN_EXT = 0x3, + FM93C56A_WRITE_ALL_EXT = 0x1, + FM93C56A_WDS_EXT = 0x0, + FM93C56A_ERASE_ALL_EXT = 0x2, +/* Special Bits */ + FM93C56A_READ_DUMMY_BITS = 1, + FM93C56A_READY = 0, + FM93C56A_BUSY = 1, + FM93C56A_CMD_BITS = 2, +/* AM29LV Flash definitions */ + FM93C56A_SIZE_8 = 0x100, + FM93C56A_SIZE_16 = 0x80, + FM93C66A_SIZE_8 = 0x200, + FM93C66A_SIZE_16 = 0x100, + FM93C86A_SIZE_16 = 0x400, +/* Address Bits */ + FM93C56A_NO_ADDR_BITS_16 = 8, + FM93C56A_NO_ADDR_BITS_8 = 9, + FM93C86A_NO_ADDR_BITS_16 = 10, +/* Data Bits */ + FM93C56A_DATA_BITS_16 = 16, + FM93C56A_DATA_BITS_8 = 8, +}; +enum { +/* Auburn Bits */ + AUBURN_EEPROM_DI = 0x8, + AUBURN_EEPROM_DI_0 = 0x0, + AUBURN_EEPROM_DI_1 = 0x8, + AUBURN_EEPROM_DO = 0x4, + AUBURN_EEPROM_DO_0 = 0x0, + AUBURN_EEPROM_DO_1 = 0x4, + AUBURN_EEPROM_CS = 0x2, + AUBURN_EEPROM_CS_0 = 0x0, + AUBURN_EEPROM_CS_1 = 0x2, + AUBURN_EEPROM_CLK_RISE = 0x1, + AUBURN_EEPROM_CLK_FALL = 0x0, +}; +enum {EEPROM_SIZE = FM93C86A_SIZE_16, + EEPROM_NO_ADDR_BITS = FM93C86A_NO_ADDR_BITS_16, + EEPROM_NO_DATA_BITS = FM93C56A_DATA_BITS_16, +}; + +/* + * MAC Config data structure + */ + struct eeprom_port_cfg { + u16 etherMtu_mac; + u16 pauseThreshold_mac; + u16 resumeThreshold_mac; + u16 portConfiguration; +#define PORT_CONFIG_AUTO_NEG_ENABLED 0x8000 +#define PORT_CONFIG_SYM_PAUSE_ENABLED 0x4000 +#define PORT_CONFIG_FULL_DUPLEX_ENABLED 0x2000 +#define PORT_CONFIG_HALF_DUPLEX_ENABLED 0x1000 +#define PORT_CONFIG_1000MB_SPEED 0x0400 +#define PORT_CONFIG_100MB_SPEED 0x0200 +#define PORT_CONFIG_10MB_SPEED 0x0100 +#define PORT_CONFIG_LINK_SPEED_MASK 0x0F00 + u16 reserved[12]; + +}; + +/* + * BIOS data structure + */ +struct eeprom_bios_cfg { + u16 SpinDlyEn:1, disBios:1, EnMemMap:1, EnSelectBoot:1, Reserved:12; + + u8 bootID0:7, boodID0Valid:1; + u8 bootLun0[8]; + + u8 bootID1:7, boodID1Valid:1; + u8 bootLun1[8]; + + u16 MaxLunsTrgt; + u8 reserved[10]; +}; + +/* + * Function Specific Data structure + */ +struct eeprom_function_cfg { + u8 reserved[30]; + u8 macAddress[6]; + u8 macAddressSecondary[6]; + + u16 subsysVendorId; + u16 subsysDeviceId; +}; + +/* + * EEPROM format + */ +struct eeprom_data { + u8 asicId[4]; + u8 version; + u8 numPorts; + u16 boardId; + +#define EEPROM_BOARDID_STR_SIZE 16 +#define EEPROM_SERIAL_NUM_SIZE 16 + + u8 boardIdStr[16]; + u8 serialNumber[16]; + u16 extHwConfig; + struct eeprom_port_cfg macCfg_port0; + struct eeprom_port_cfg macCfg_port1; + u16 bufletSize; + u16 bufletCount; + u16 tcpWindowThreshold50; + u16 tcpWindowThreshold25; + u16 tcpWindowThreshold0; + u16 ipHashTableBaseHi; + u16 ipHashTableBaseLo; + u16 ipHashTableSize; + u16 tcpHashTableBaseHi; + u16 tcpHashTableBaseLo; + u16 tcpHashTableSize; + u16 ncbTableBaseHi; + u16 ncbTableBaseLo; + u16 ncbTableSize; + u16 drbTableBaseHi; + u16 drbTableBaseLo; + u16 drbTableSize; + u16 reserved_142[4]; + u16 ipReassemblyTimeout; + u16 tcpMaxWindowSize; + u16 ipSecurity; +#define IPSEC_CONFIG_PRESENT 0x0001 + u8 reserved_156[294]; + u16 qDebug[8]; + struct eeprom_function_cfg funcCfg_fn0; + u16 reserved_510; + u8 oemSpace[432]; + struct eeprom_bios_cfg biosCfg_fn1; + struct eeprom_function_cfg funcCfg_fn1; + u16 reserved_1022; + u8 reserved_1024[464]; + struct eeprom_function_cfg funcCfg_fn2; + u16 reserved_1534; + u8 reserved_1536[432]; + struct eeprom_bios_cfg biosCfg_fn3; + struct eeprom_function_cfg funcCfg_fn3; + u16 checksum; +}; + +/* + * General definitions... + */ + +/* + * Below are a number compiler switches for controlling driver behavior. + * Some are not supported under certain conditions and are notated as such. + */ + +#define QL3XXX_VENDOR_ID 0x1077 +#define QL3022_DEVICE_ID 0x3022 + +/* MTU & Frame Size stuff */ +#define NORMAL_MTU_SIZE ETH_DATA_LEN +#define JUMBO_MTU_SIZE 9000 +#define VLAN_ID_LEN 2 + +/* Request Queue Related Definitions */ +#define NUM_REQ_Q_ENTRIES 256 /* so that 64 * 64 = 4096 (1 page) */ + +/* Response Queue Related Definitions */ +#define NUM_RSP_Q_ENTRIES 256 /* so that 256 * 16 = 4096 (1 page) */ + +/* Transmit and Receive Buffers */ +#define NUM_LBUFQ_ENTRIES 128 +#define NUM_SBUFQ_ENTRIES 64 +#define QL_SMALL_BUFFER_SIZE 32 +#define QL_ADDR_ELE_PER_BUFQ_ENTRY \ +(sizeof(struct lrg_buf_q_entry) / sizeof(struct bufq_addr_element)) + /* Each send has at least control block. This is how many we keep. */ +#define NUM_SMALL_BUFFERS NUM_SBUFQ_ENTRIES * QL_ADDR_ELE_PER_BUFQ_ENTRY +#define NUM_LARGE_BUFFERS NUM_LBUFQ_ENTRIES * QL_ADDR_ELE_PER_BUFQ_ENTRY +#define QL_HEADER_SPACE 32 /* make header space at top of skb. */ +/* + * Large & Small Buffers for Receives + */ +struct lrg_buf_q_entry { + + u32 addr0_lower; +#define IAL_LAST_ENTRY 0x00000001 +#define IAL_CONT_ENTRY 0x00000002 +#define IAL_FLAG_MASK 0x00000003 + u32 addr0_upper; + u32 addr1_lower; + u32 addr1_upper; + u32 addr2_lower; + u32 addr2_upper; + u32 addr3_lower; + u32 addr3_upper; + u32 addr4_lower; + u32 addr4_upper; + u32 addr5_lower; + u32 addr5_upper; + u32 addr6_lower; + u32 addr6_upper; + u32 addr7_lower; + u32 addr7_upper; + +}; + +struct bufq_addr_element { + u32 addr_low; + u32 addr_high; +}; + +#define QL_NO_RESET 0 +#define QL_DO_RESET 1 + +enum link_state_t { + LS_UNKNOWN = 0, + LS_DOWN, + LS_DEGRADE, + LS_RECOVER, + LS_UP, +}; + +struct ql_rcv_buf_cb { + struct ql_rcv_buf_cb *next; + struct sk_buff *skb; + DECLARE_PCI_UNMAP_ADDR(mapaddr); + DECLARE_PCI_UNMAP_LEN(maplen); + __le32 buf_phy_addr_low; + __le32 buf_phy_addr_high; + int index; +}; + +struct ql_tx_buf_cb { + struct sk_buff *skb; + struct ob_mac_iocb_req *queue_entry ; + DECLARE_PCI_UNMAP_ADDR(mapaddr); + DECLARE_PCI_UNMAP_LEN(maplen); +}; + +/* definitions for type field */ +#define QL_BUF_TYPE_MACIOCB 0x01 +#define QL_BUF_TYPE_IPIOCB 0x02 +#define QL_BUF_TYPE_TCPIOCB 0x03 + +/* qdev->flags definitions. */ +enum { QL_RESET_DONE = 1, /* Reset finished. */ + QL_RESET_ACTIVE = 2, /* Waiting for reset to finish. */ + QL_RESET_START = 3, /* Please reset the chip. */ + QL_RESET_PER_SCSI = 4, /* SCSI driver requests reset. */ + QL_TX_TIMEOUT = 5, /* Timeout in progress. */ + QL_LINK_MASTER = 6, /* This driver controls the link. */ + QL_ADAPTER_UP = 7, /* Adapter has been brought up. */ + QL_THREAD_UP = 8, /* This flag is available. */ + QL_LINK_UP = 9, /* Link Status. */ + QL_ALLOC_REQ_RSP_Q_DONE = 10, + QL_ALLOC_BUFQS_DONE = 11, + QL_ALLOC_SMALL_BUF_DONE = 12, + QL_LINK_OPTICAL = 13, + QL_MSI_ENABLED = 14, +}; + +/* + * ql3_adapter - The main Adapter structure definition. + * This structure has all fields relevant to the hardware. + */ + +struct ql3_adapter { + u32 reserved_00; + unsigned long flags; + + /* PCI Configuration information for this device */ + struct pci_dev *pdev; + struct net_device *ndev; /* Parent NET device */ + + /* Hardware information */ + u8 chip_rev_id; + u8 pci_slot; + u8 pci_width; + u8 pci_x; + u32 msi; + int index; + struct timer_list adapter_timer; /* timer used for various functions */ + + spinlock_t adapter_lock; + spinlock_t hw_lock; + + /* PCI Bus Relative Register Addresses */ + u8 *mmap_virt_base; /* stores return value from ioremap() */ + struct ql3xxx_port_registers __iomem *mem_map_registers; + u32 current_page; /* tracks current register page */ + + u32 msg_enable; + u8 reserved_01[2]; + u8 reserved_02[2]; + + /* Page for Shadow Registers */ + void *shadow_reg_virt_addr; + dma_addr_t shadow_reg_phy_addr; + + /* Net Request Queue */ + u32 req_q_size; + u32 reserved_03; + struct ob_mac_iocb_req *req_q_virt_addr; + dma_addr_t req_q_phy_addr; + u16 req_producer_index; + u16 reserved_04; + u16 *preq_consumer_index; + u32 req_consumer_index_phy_addr_high; + u32 req_consumer_index_phy_addr_low; + atomic_t tx_count; + struct ql_tx_buf_cb tx_buf[NUM_REQ_Q_ENTRIES]; + + /* Net Response Queue */ + u32 rsp_q_size; + u32 eeprom_cmd_data; + struct net_rsp_iocb *rsp_q_virt_addr; + dma_addr_t rsp_q_phy_addr; + struct net_rsp_iocb *rsp_current; + u16 rsp_consumer_index; + u16 reserved_06; + u32 *prsp_producer_index; + u32 rsp_producer_index_phy_addr_high; + u32 rsp_producer_index_phy_addr_low; + + /* Large Buffer Queue */ + u32 lrg_buf_q_alloc_size; + u32 lrg_buf_q_size; + void *lrg_buf_q_alloc_virt_addr; + void *lrg_buf_q_virt_addr; + dma_addr_t lrg_buf_q_alloc_phy_addr; + dma_addr_t lrg_buf_q_phy_addr; + u32 lrg_buf_q_producer_index; + u32 lrg_buf_release_cnt; + struct bufq_addr_element *lrg_buf_next_free; + + /* Large (Receive) Buffers */ + struct ql_rcv_buf_cb lrg_buf[NUM_LARGE_BUFFERS]; + struct ql_rcv_buf_cb *lrg_buf_free_head; + struct ql_rcv_buf_cb *lrg_buf_free_tail; + u32 lrg_buf_free_count; + u32 lrg_buffer_len; + u32 lrg_buf_index; + u32 lrg_buf_skb_check; + + /* Small Buffer Queue */ + u32 small_buf_q_alloc_size; + u32 small_buf_q_size; + u32 small_buf_q_producer_index; + void *small_buf_q_alloc_virt_addr; + void *small_buf_q_virt_addr; + dma_addr_t small_buf_q_alloc_phy_addr; + dma_addr_t small_buf_q_phy_addr; + u32 small_buf_index; + + /* Small (Receive) Buffers */ + void *small_buf_virt_addr; + dma_addr_t small_buf_phy_addr; + u32 small_buf_phy_addr_low; + u32 small_buf_phy_addr_high; + u32 small_buf_release_cnt; + u32 small_buf_total_size; + + /* ISR related, saves status for DPC. */ + u32 control_status; + + struct eeprom_data nvram_data; + struct timer_list ioctl_timer; + u32 port_link_state; + u32 last_rsp_offset; + + /* 4022 specific */ + u32 mac_index; /* Driver's MAC number can be 0 or 1 for first and second networking functions respectively */ + u32 PHYAddr; /* Address of PHY 0x1e00 Port 0 and 0x1f00 Port 1 */ + u32 mac_ob_opcode; /* Opcode to use on mac transmission */ + u32 tcp_ob_opcode; /* Opcode to use on tcp transmission */ + u32 update_ob_opcode; /* Opcode to use for updating NCB */ + u32 mb_bit_mask; /* MA Bits mask to use on transmission */ + u32 numPorts; + struct net_device_stats stats; + struct workqueue_struct *workqueue; + struct work_struct reset_work; + struct work_struct tx_timeout_work; + u32 max_frame_size; +}; + +#endif /* _QLA3XXX_H_ */ diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index de91609ca11..13431f6a3ad 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -50,7 +50,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "1.5" +#define DRV_VERSION "1.6" #define PFX DRV_NAME " " /* @@ -121,6 +121,11 @@ static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4361) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4362) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4363) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4364) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4365) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4366) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4367) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4368) }, { 0 } }; @@ -1927,12 +1932,6 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last) } } -/* Is status ring empty or is there more to do? */ -static inline int sky2_more_work(const struct sky2_hw *hw) -{ - return (hw->st_idx != sky2_read16(hw, STAT_PUT_IDX)); -} - /* Process status response ring */ static int sky2_status_intr(struct sky2_hw *hw, int to_do) { @@ -2023,6 +2022,9 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do) } } + /* Fully processed status ring so clear irq */ + sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); + exit_loop: if (buf_write[0]) { sky2 = netdev_priv(hw->dev[0]); @@ -2232,19 +2234,16 @@ static int sky2_poll(struct net_device *dev0, int *budget) sky2_descriptor_error(hw, 1, "transmit", Y2_IS_CHK_TXA2); work_done = sky2_status_intr(hw, work_limit); - *budget -= work_done; - dev0->quota -= work_done; - - if (status & Y2_IS_STAT_BMU) - sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); + if (work_done < work_limit) { + netif_rx_complete(dev0); - if (sky2_more_work(hw)) + sky2_read32(hw, B0_Y2_SP_LISR); + return 0; + } else { + *budget -= work_done; + dev0->quota -= work_done; return 1; - - netif_rx_complete(dev0); - - sky2_read32(hw, B0_Y2_SP_LISR); - return 0; + } } static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) diff --git a/drivers/net/slhc.c b/drivers/net/slhc.c index 3a1b7131681..9a540e2092b 100644 --- a/drivers/net/slhc.c +++ b/drivers/net/slhc.c @@ -94,27 +94,23 @@ slhc_init(int rslots, int tslots) register struct cstate *ts; struct slcompress *comp; - comp = (struct slcompress *)kmalloc(sizeof(struct slcompress), - GFP_KERNEL); + comp = kzalloc(sizeof(struct slcompress), GFP_KERNEL); if (! comp) goto out_fail; - memset(comp, 0, sizeof(struct slcompress)); if ( rslots > 0 && rslots < 256 ) { size_t rsize = rslots * sizeof(struct cstate); - comp->rstate = (struct cstate *) kmalloc(rsize, GFP_KERNEL); + comp->rstate = kzalloc(rsize, GFP_KERNEL); if (! comp->rstate) goto out_free; - memset(comp->rstate, 0, rsize); comp->rslot_limit = rslots - 1; } if ( tslots > 0 && tslots < 256 ) { size_t tsize = tslots * sizeof(struct cstate); - comp->tstate = (struct cstate *) kmalloc(tsize, GFP_KERNEL); + comp->tstate = kzalloc(tsize, GFP_KERNEL); if (! comp->tstate) goto out_free2; - memset(comp->tstate, 0, tsize); comp->tslot_limit = tslots - 1; } @@ -141,9 +137,9 @@ slhc_init(int rslots, int tslots) return comp; out_free2: - kfree((unsigned char *)comp->rstate); + kfree(comp->rstate); out_free: - kfree((unsigned char *)comp); + kfree(comp); out_fail: return NULL; } @@ -700,20 +696,6 @@ EXPORT_SYMBOL(slhc_compress); EXPORT_SYMBOL(slhc_uncompress); EXPORT_SYMBOL(slhc_toss); -#ifdef MODULE - -int init_module(void) -{ - printk(KERN_INFO "CSLIP: code copyright 1989 Regents of the University of California\n"); - return 0; -} - -void cleanup_module(void) -{ - return; -} - -#endif /* MODULE */ #else /* CONFIG_INET */ diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/tulip/uli526x.c index fd64b2b3e99..c4c720e2d4c 100644 --- a/drivers/net/tulip/uli526x.c +++ b/drivers/net/tulip/uli526x.c @@ -1702,7 +1702,6 @@ MODULE_PARM_DESC(mode, "ULi M5261/M5263: Bit 0: 10/100Mbps, bit 2: duplex, bit 8 static int __init uli526x_init_module(void) { - int rc; printk(version); printed_version = 1; @@ -1714,22 +1713,19 @@ static int __init uli526x_init_module(void) if (cr6set) uli526x_cr6_user_set = cr6set; - switch(mode) { + switch (mode) { case ULI526X_10MHF: case ULI526X_100MHF: case ULI526X_10MFD: case ULI526X_100MFD: uli526x_media_mode = mode; break; - default:uli526x_media_mode = ULI526X_AUTO; + default: + uli526x_media_mode = ULI526X_AUTO; break; } - rc = pci_module_init(&uli526x_driver); - if (rc < 0) - return rc; - - return 0; + return pci_register_driver(&uli526x_driver); } diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index a4dd1394271..16befbcea58 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -3950,13 +3950,11 @@ static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) { pRsp->rsp0 = IN4500(ai, RESP0); pRsp->rsp1 = IN4500(ai, RESP1); pRsp->rsp2 = IN4500(ai, RESP2); - if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET) { - airo_print_err(ai->dev->name, "cmd= %x\n", pCmd->cmd); - airo_print_err(ai->dev->name, "status= %x\n", pRsp->status); - airo_print_err(ai->dev->name, "Rsp0= %x\n", pRsp->rsp0); - airo_print_err(ai->dev->name, "Rsp1= %x\n", pRsp->rsp1); - airo_print_err(ai->dev->name, "Rsp2= %x\n", pRsp->rsp2); - } + if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET) + airo_print_err(ai->dev->name, + "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x", + pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1, + pRsp->rsp2); // clear stuck command busy if necessary if (IN4500(ai, COMMAND) & COMMAND_BUSY) { diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h index 17a56828e23..c6ee1e974c8 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx.h @@ -504,6 +504,12 @@ struct bcm43xx_phyinfo { * This lock is only used by bcm43xx_phy_{un}lock() */ spinlock_t lock; + + /* Firmware. */ + const struct firmware *ucode; + const struct firmware *pcm; + const struct firmware *initvals0; + const struct firmware *initvals1; }; @@ -593,12 +599,14 @@ struct bcm43xx_coreinfo { u8 available:1, enabled:1, initialized:1; - /** core_id ID number */ - u16 id; /** core_rev revision number */ u8 rev; /** Index number for _switch_core() */ u8 index; + /** core_id ID number */ + u16 id; + /** Core-specific data. */ + void *priv; }; /* Additional information for each 80211 core. */ @@ -647,7 +655,23 @@ enum { BCM43xx_STAT_RESTARTING, /* controller_restart() called. */ }; #define bcm43xx_status(bcm) atomic_read(&(bcm)->init_status) -#define bcm43xx_set_status(bcm, stat) atomic_set(&(bcm)->init_status, (stat)) +#define bcm43xx_set_status(bcm, stat) do { \ + atomic_set(&(bcm)->init_status, (stat)); \ + smp_wmb(); \ + } while (0) + +/* *** THEORY OF LOCKING *** + * + * We have two different locks in the bcm43xx driver. + * => bcm->mutex: General sleeping mutex. Protects struct bcm43xx_private + * and the device registers. This mutex does _not_ protect + * against concurrency from the IRQ handler. + * => bcm->irq_lock: IRQ spinlock. Protects against IRQ handler concurrency. + * + * Please note that, if you only take the irq_lock, you are not protected + * against concurrency from the periodic work handlers. + * Most times you want to take _both_ locks. + */ struct bcm43xx_private { struct ieee80211_device *ieee; @@ -659,7 +683,6 @@ struct bcm43xx_private { void __iomem *mmio_addr; - /* Locking, see "theory of locking" text below. */ spinlock_t irq_lock; struct mutex mutex; @@ -691,6 +714,7 @@ struct bcm43xx_private { struct bcm43xx_sprominfo sprom; #define BCM43xx_NR_LEDS 4 struct bcm43xx_led leds[BCM43xx_NR_LEDS]; + spinlock_t leds_lock; /* The currently active core. */ struct bcm43xx_coreinfo *current_core; @@ -708,10 +732,6 @@ struct bcm43xx_private { struct bcm43xx_coreinfo core_80211[ BCM43xx_MAX_80211_CORES ]; /* Additional information, specific to the 80211 cores. */ struct bcm43xx_coreinfo_80211 core_80211_ext[ BCM43xx_MAX_80211_CORES ]; - /* Index of the current 80211 core. If current_core is not - * an 80211 core, this is -1. - */ - int current_80211_core_idx; /* Number of available 80211 cores. */ int nr_80211_available; @@ -724,6 +744,8 @@ struct bcm43xx_private { u32 irq_savedstate; /* Link Quality calculation context. */ struct bcm43xx_noise_calculation noisecalc; + /* if > 0 MAC is suspended. if == 0 MAC is enabled. */ + int mac_suspended; /* Threshold values. */ //TODO: The RTS thr has to be _used_. Currently, it is only set via WX. @@ -746,12 +768,6 @@ struct bcm43xx_private { struct bcm43xx_key key[54]; u8 default_key_idx; - /* Firmware. */ - const struct firmware *ucode; - const struct firmware *pcm; - const struct firmware *initvals0; - const struct firmware *initvals1; - /* Random Number Generator. */ struct hwrng rng; char rng_name[20 + 1]; @@ -763,55 +779,6 @@ struct bcm43xx_private { }; -/* *** THEORY OF LOCKING *** - * - * We have two different locks in the bcm43xx driver. - * => bcm->mutex: General sleeping mutex. Protects struct bcm43xx_private - * and the device registers. - * => bcm->irq_lock: IRQ spinlock. Protects against IRQ handler concurrency. - * - * We have three types of helper function pairs to utilize these locks. - * (Always use the helper functions.) - * 1) bcm43xx_{un}lock_noirq(): - * Takes bcm->mutex. Does _not_ protect against IRQ concurrency, - * so it is almost always unsafe, if device IRQs are enabled. - * So only use this, if device IRQs are masked. - * Locking may sleep. - * You can sleep within the critical section. - * 2) bcm43xx_{un}lock_irqonly(): - * Takes bcm->irq_lock. Does _not_ protect against - * bcm43xx_lock_noirq() critical sections. - * Does only protect against the IRQ handler path and other - * irqonly() critical sections. - * Locking does not sleep. - * You must not sleep within the critical section. - * 3) bcm43xx_{un}lock_irqsafe(): - * This is the cummulative lock and takes both, mutex and irq_lock. - * Protects against noirq() and irqonly() critical sections (and - * the IRQ handler path). - * Locking may sleep. - * You must not sleep within the critical section. - */ - -/* Lock type 1 */ -#define bcm43xx_lock_noirq(bcm) mutex_lock(&(bcm)->mutex) -#define bcm43xx_unlock_noirq(bcm) mutex_unlock(&(bcm)->mutex) -/* Lock type 2 */ -#define bcm43xx_lock_irqonly(bcm, flags) \ - spin_lock_irqsave(&(bcm)->irq_lock, flags) -#define bcm43xx_unlock_irqonly(bcm, flags) \ - spin_unlock_irqrestore(&(bcm)->irq_lock, flags) -/* Lock type 3 */ -#define bcm43xx_lock_irqsafe(bcm, flags) do { \ - bcm43xx_lock_noirq(bcm); \ - bcm43xx_lock_irqonly(bcm, flags); \ - } while (0) -#define bcm43xx_unlock_irqsafe(bcm, flags) do { \ - bcm43xx_unlock_irqonly(bcm, flags); \ - bcm43xx_unlock_noirq(bcm); \ - } while (0) - - static inline struct bcm43xx_private * bcm43xx_priv(struct net_device *dev) { @@ -863,34 +830,33 @@ int bcm43xx_using_pio(struct bcm43xx_private *bcm) * any of these functions. */ static inline +struct bcm43xx_coreinfo_80211 * +bcm43xx_current_80211_priv(struct bcm43xx_private *bcm) +{ + assert(bcm->current_core->id == BCM43xx_COREID_80211); + return bcm->current_core->priv; +} +static inline struct bcm43xx_pio * bcm43xx_current_pio(struct bcm43xx_private *bcm) { assert(bcm43xx_using_pio(bcm)); - assert(bcm->current_80211_core_idx >= 0); - assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); - return &(bcm->core_80211_ext[bcm->current_80211_core_idx].pio); + return &(bcm43xx_current_80211_priv(bcm)->pio); } static inline struct bcm43xx_dma * bcm43xx_current_dma(struct bcm43xx_private *bcm) { assert(!bcm43xx_using_pio(bcm)); - assert(bcm->current_80211_core_idx >= 0); - assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); - return &(bcm->core_80211_ext[bcm->current_80211_core_idx].dma); + return &(bcm43xx_current_80211_priv(bcm)->dma); } static inline struct bcm43xx_phyinfo * bcm43xx_current_phy(struct bcm43xx_private *bcm) { - assert(bcm->current_80211_core_idx >= 0); - assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); - return &(bcm->core_80211_ext[bcm->current_80211_core_idx].phy); + return &(bcm43xx_current_80211_priv(bcm)->phy); } static inline struct bcm43xx_radioinfo * bcm43xx_current_radio(struct bcm43xx_private *bcm) { - assert(bcm->current_80211_core_idx >= 0); - assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); - return &(bcm->core_80211_ext[bcm->current_80211_core_idx].radio); + return &(bcm43xx_current_80211_priv(bcm)->radio); } diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c index ce2e40b29b4..923275ea078 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c @@ -77,7 +77,8 @@ static ssize_t devinfo_read_file(struct file *file, char __user *userbuf, down(&big_buffer_sem); - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) { fappend("Board not initialized.\n"); goto out; @@ -121,7 +122,8 @@ static ssize_t devinfo_read_file(struct file *file, char __user *userbuf, fappend("\n"); out: - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); up(&big_buffer_sem); return res; @@ -159,7 +161,8 @@ static ssize_t spromdump_read_file(struct file *file, char __user *userbuf, unsigned long flags; down(&big_buffer_sem); - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) { fappend("Board not initialized.\n"); goto out; @@ -169,7 +172,8 @@ static ssize_t spromdump_read_file(struct file *file, char __user *userbuf, fappend("boardflags: 0x%04x\n", bcm->sprom.boardflags); out: - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); up(&big_buffer_sem); return res; @@ -188,7 +192,8 @@ static ssize_t tsf_read_file(struct file *file, char __user *userbuf, u64 tsf; down(&big_buffer_sem); - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) { fappend("Board not initialized.\n"); goto out; @@ -199,7 +204,8 @@ static ssize_t tsf_read_file(struct file *file, char __user *userbuf, (unsigned int)(tsf & 0xFFFFFFFFULL)); out: - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); up(&big_buffer_sem); return res; @@ -221,7 +227,8 @@ static ssize_t tsf_write_file(struct file *file, const char __user *user_buf, res = -EFAULT; goto out_up; } - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) { printk(KERN_INFO PFX "debugfs: Board not initialized.\n"); res = -EFAULT; @@ -237,7 +244,8 @@ static ssize_t tsf_write_file(struct file *file, const char __user *user_buf, res = buf_size; out_unlock: - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); out_up: up(&big_buffer_sem); return res; @@ -258,7 +266,8 @@ static ssize_t txstat_read_file(struct file *file, char __user *userbuf, int i, cnt, j = 0; down(&big_buffer_sem); - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); fappend("Last %d logged xmitstatus blobs (Latest first):\n\n", BCM43xx_NR_LOGGED_XMITSTATUS); @@ -294,14 +303,51 @@ static ssize_t txstat_read_file(struct file *file, char __user *userbuf, i = BCM43xx_NR_LOGGED_XMITSTATUS - 1; } - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - bcm43xx_lock_irqsafe(bcm, flags); + spin_lock_irqsave(&bcm->irq_lock, flags); if (*ppos == pos) { /* Done. Drop the copied data. */ e->xmitstatus_printing = 0; } - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); + up(&big_buffer_sem); + return res; +} + +static ssize_t restart_write_file(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct bcm43xx_private *bcm = file->private_data; + char *buf = really_big_buffer; + ssize_t buf_size; + ssize_t res; + unsigned long flags; + + buf_size = min(count, sizeof (really_big_buffer) - 1); + down(&big_buffer_sem); + if (copy_from_user(buf, user_buf, buf_size)) { + res = -EFAULT; + goto out_up; + } + mutex_lock(&(bcm)->mutex); + spin_lock_irqsave(&(bcm)->irq_lock, flags); + if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) { + printk(KERN_INFO PFX "debugfs: Board not initialized.\n"); + res = -EFAULT; + goto out_unlock; + } + if (count > 0 && buf[0] == '1') { + bcm43xx_controller_restart(bcm, "manually restarted"); + res = count; + } else + res = -EINVAL; + +out_unlock: + spin_unlock_irqrestore(&(bcm)->irq_lock, flags); + mutex_unlock(&(bcm)->mutex); +out_up: up(&big_buffer_sem); return res; } @@ -339,6 +385,11 @@ static struct file_operations txstat_fops = { .open = open_file_generic, }; +static struct file_operations restart_fops = { + .write = restart_write_file, + .open = open_file_generic, +}; + void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm) { @@ -390,6 +441,10 @@ void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm) bcm, &txstat_fops); if (!e->dentry_txstat) printk(KERN_ERR PFX "debugfs: creating \"tx_status\" for \"%s\" failed!\n", devdir); + e->dentry_restart = debugfs_create_file("restart", 0222, e->subdir, + bcm, &restart_fops); + if (!e->dentry_restart) + printk(KERN_ERR PFX "debugfs: creating \"restart\" for \"%s\" failed!\n", devdir); } void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm) @@ -405,6 +460,7 @@ void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm) debugfs_remove(e->dentry_devinfo); debugfs_remove(e->dentry_tsf); debugfs_remove(e->dentry_txstat); + debugfs_remove(e->dentry_restart); debugfs_remove(e->subdir); kfree(e->xmitstatus_buffer); kfree(e->xmitstatus_print_buffer); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h index 50ce267f794..a40d1af3554 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h @@ -20,6 +20,7 @@ struct bcm43xx_dfsentry { struct dentry *dentry_spromdump; struct dentry *dentry_tsf; struct dentry *dentry_txstat; + struct dentry *dentry_restart; struct bcm43xx_private *bcm; diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_leds.c b/drivers/net/wireless/bcm43xx/bcm43xx_leds.c index ec80692d638..c3f90c8563d 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_leds.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_leds.c @@ -51,12 +51,12 @@ static void bcm43xx_led_blink(unsigned long d) struct bcm43xx_private *bcm = led->bcm; unsigned long flags; - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&bcm->leds_lock, flags); if (led->blink_interval) { bcm43xx_led_changestate(led); mod_timer(&led->blink_timer, jiffies + led->blink_interval); } - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->leds_lock, flags); } static void bcm43xx_led_blink_start(struct bcm43xx_led *led, @@ -177,7 +177,9 @@ void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity) int i, turn_on; unsigned long interval = 0; u16 ledctl; + unsigned long flags; + spin_lock_irqsave(&bcm->leds_lock, flags); ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); for (i = 0; i < BCM43xx_NR_LEDS; i++) { led = &(bcm->leds[i]); @@ -266,6 +268,7 @@ void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity) ledctl &= ~(1 << i); } bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); + spin_unlock_irqrestore(&bcm->leds_lock, flags); } void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on) @@ -274,7 +277,9 @@ void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on) u16 ledctl; int i; int bit_on; + unsigned long flags; + spin_lock_irqsave(&bcm->leds_lock, flags); ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); for (i = 0; i < BCM43xx_NR_LEDS; i++) { led = &(bcm->leds[i]); @@ -290,4 +295,5 @@ void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on) ledctl &= ~(1 << i); } bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); + spin_unlock_irqrestore(&bcm->leds_lock, flags); } diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index df317c1e12a..b095f3cc673 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -509,23 +509,19 @@ static void bcm43xx_synchronize_irq(struct bcm43xx_private *bcm) } /* Make sure we don't receive more data from the device. */ -static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate) +static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm) { unsigned long flags; - u32 old; - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&bcm->irq_lock, flags); if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) { - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); return -EBUSY; } - old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); - bcm43xx_unlock_irqonly(bcm, flags); + bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); + spin_unlock_irqrestore(&bcm->irq_lock, flags); bcm43xx_synchronize_irq(bcm); - if (oldstate) - *oldstate = old; - return 0; } @@ -537,7 +533,6 @@ static int bcm43xx_read_radioinfo(struct bcm43xx_private *bcm) u16 manufact; u16 version; u8 revision; - s8 i; if (bcm->chip_id == 0x4317) { if (bcm->chip_rev == 0x00) @@ -580,20 +575,11 @@ static int bcm43xx_read_radioinfo(struct bcm43xx_private *bcm) radio->version = version; radio->revision = revision; - /* Set default attenuation values. */ - radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm); - radio->radio_atten = bcm43xx_default_radio_attenuation(bcm); - radio->txctl1 = bcm43xx_default_txctl1(bcm); - radio->txctl2 = 0xFFFF; if (phy->type == BCM43xx_PHYTYPE_A) radio->txpower_desired = bcm->sprom.maxpower_aphy; else radio->txpower_desired = bcm->sprom.maxpower_bgphy; - /* Initialize the in-memory nrssi Lookup Table. */ - for (i = 0; i < 64; i++) - radio->nrssi_lt[i] = i; - return 0; err_unsupported_radio: @@ -1250,10 +1236,6 @@ int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *ne goto out; bcm->current_core = new_core; - bcm->current_80211_core_idx = -1; - if (new_core->id == BCM43xx_COREID_80211) - bcm->current_80211_core_idx = (int)(new_core - &(bcm->core_80211[0])); - out: return err; } @@ -1423,43 +1405,23 @@ static void bcm43xx_wireless_core_disable(struct bcm43xx_private *bcm) bcm43xx_core_disable(bcm, 0); } -/* Mark the current 80211 core inactive. - * "active_80211_core" is the other 80211 core, which is used. - */ -static int bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm, - struct bcm43xx_coreinfo *active_80211_core) +/* Mark the current 80211 core inactive. */ +static void bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm) { u32 sbtmstatelow; - struct bcm43xx_coreinfo *old_core; - int err = 0; bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); bcm43xx_radio_turn_off(bcm); sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - sbtmstatelow &= ~0x200a0000; - sbtmstatelow |= 0xa0000; + sbtmstatelow &= 0xDFF5FFFF; + sbtmstatelow |= 0x000A0000; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); udelay(1); sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - sbtmstatelow &= ~0xa0000; - sbtmstatelow |= 0x80000; + sbtmstatelow &= 0xFFF5FFFF; + sbtmstatelow |= 0x00080000; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); udelay(1); - - if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) { - old_core = bcm->current_core; - err = bcm43xx_switch_core(bcm, active_80211_core); - if (err) - goto out; - sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - sbtmstatelow &= ~0x20000000; - sbtmstatelow |= 0x20000000; - bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); - err = bcm43xx_switch_core(bcm, old_core); - } - -out: - return err; } static void handle_irq_transmit_status(struct bcm43xx_private *bcm) @@ -1720,7 +1682,7 @@ static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) # define bcmirq_handled(irq) do { /* nothing */ } while (0) #endif /* CONFIG_BCM43XX_DEBUG*/ - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&bcm->irq_lock, flags); reason = bcm->irq_reason; dma_reason[0] = bcm->dma_reason[0]; dma_reason[1] = bcm->dma_reason[1]; @@ -1746,7 +1708,7 @@ static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) dma_reason[2], dma_reason[3]); bcm43xx_controller_restart(bcm, "DMA error"); mmiowb(); - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); return; } if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_NONFATALMASK) | @@ -1834,7 +1796,7 @@ static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) bcm43xx_leds_update(bcm, activity); bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate); mmiowb(); - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); } static void pio_irq_workaround(struct bcm43xx_private *bcm, @@ -1885,14 +1847,8 @@ static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_re spin_lock(&bcm->irq_lock); - /* Only accept IRQs, if we are initialized properly. - * This avoids an RX race while initializing. - * We should probably not enable IRQs before we are initialized - * completely, but some careful work is needed to fix this. I think it - * is best to stay with this cheap workaround for now... . - */ - if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) - goto out; + assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); + assert(bcm->current_core->id == BCM43xx_COREID_80211); reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); if (reason == 0xffffffff) { @@ -1930,16 +1886,18 @@ out: static void bcm43xx_release_firmware(struct bcm43xx_private *bcm, int force) { + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + if (bcm->firmware_norelease && !force) return; /* Suspending or controller reset. */ - release_firmware(bcm->ucode); - bcm->ucode = NULL; - release_firmware(bcm->pcm); - bcm->pcm = NULL; - release_firmware(bcm->initvals0); - bcm->initvals0 = NULL; - release_firmware(bcm->initvals1); - bcm->initvals1 = NULL; + release_firmware(phy->ucode); + phy->ucode = NULL; + release_firmware(phy->pcm); + phy->pcm = NULL; + release_firmware(phy->initvals0); + phy->initvals0 = NULL; + release_firmware(phy->initvals1); + phy->initvals1 = NULL; } static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) @@ -1950,11 +1908,11 @@ static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) int nr; char buf[22 + sizeof(modparam_fwpostfix) - 1] = { 0 }; - if (!bcm->ucode) { + if (!phy->ucode) { snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_microcode%d%s.fw", (rev >= 5 ? 5 : rev), modparam_fwpostfix); - err = request_firmware(&bcm->ucode, buf, &bcm->pci_dev->dev); + err = request_firmware(&phy->ucode, buf, &bcm->pci_dev->dev); if (err) { printk(KERN_ERR PFX "Error: Microcode \"%s\" not available or load failed.\n", @@ -1963,12 +1921,12 @@ static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) } } - if (!bcm->pcm) { + if (!phy->pcm) { snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_pcm%d%s.fw", (rev < 5 ? 4 : 5), modparam_fwpostfix); - err = request_firmware(&bcm->pcm, buf, &bcm->pci_dev->dev); + err = request_firmware(&phy->pcm, buf, &bcm->pci_dev->dev); if (err) { printk(KERN_ERR PFX "Error: PCM \"%s\" not available or load failed.\n", @@ -1977,7 +1935,7 @@ static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) } } - if (!bcm->initvals0) { + if (!phy->initvals0) { if (rev == 2 || rev == 4) { switch (phy->type) { case BCM43xx_PHYTYPE_A: @@ -2008,20 +1966,20 @@ static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw", nr, modparam_fwpostfix); - err = request_firmware(&bcm->initvals0, buf, &bcm->pci_dev->dev); + err = request_firmware(&phy->initvals0, buf, &bcm->pci_dev->dev); if (err) { printk(KERN_ERR PFX "Error: InitVals \"%s\" not available or load failed.\n", buf); goto error; } - if (bcm->initvals0->size % sizeof(struct bcm43xx_initval)) { + if (phy->initvals0->size % sizeof(struct bcm43xx_initval)) { printk(KERN_ERR PFX "InitVals fileformat error.\n"); goto error; } } - if (!bcm->initvals1) { + if (!phy->initvals1) { if (rev >= 5) { u32 sbtmstatehigh; @@ -2043,14 +2001,14 @@ static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw", nr, modparam_fwpostfix); - err = request_firmware(&bcm->initvals1, buf, &bcm->pci_dev->dev); + err = request_firmware(&phy->initvals1, buf, &bcm->pci_dev->dev); if (err) { printk(KERN_ERR PFX "Error: InitVals \"%s\" not available or load failed.\n", buf); goto error; } - if (bcm->initvals1->size % sizeof(struct bcm43xx_initval)) { + if (phy->initvals1->size % sizeof(struct bcm43xx_initval)) { printk(KERN_ERR PFX "InitVals fileformat error.\n"); goto error; } @@ -2070,12 +2028,13 @@ err_noinitval: static void bcm43xx_upload_microcode(struct bcm43xx_private *bcm) { + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); const u32 *data; unsigned int i, len; /* Upload Microcode. */ - data = (u32 *)(bcm->ucode->data); - len = bcm->ucode->size / sizeof(u32); + data = (u32 *)(phy->ucode->data); + len = phy->ucode->size / sizeof(u32); bcm43xx_shm_control_word(bcm, BCM43xx_SHM_UCODE, 0x0000); for (i = 0; i < len; i++) { bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, @@ -2084,8 +2043,8 @@ static void bcm43xx_upload_microcode(struct bcm43xx_private *bcm) } /* Upload PCM data. */ - data = (u32 *)(bcm->pcm->data); - len = bcm->pcm->size / sizeof(u32); + data = (u32 *)(phy->pcm->data); + len = phy->pcm->size / sizeof(u32); bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01ea); bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, 0x00004000); bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01eb); @@ -2131,15 +2090,16 @@ err_format: static int bcm43xx_upload_initvals(struct bcm43xx_private *bcm) { + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); int err; - err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals0->data, - bcm->initvals0->size / sizeof(struct bcm43xx_initval)); + err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)phy->initvals0->data, + phy->initvals0->size / sizeof(struct bcm43xx_initval)); if (err) goto out; - if (bcm->initvals1) { - err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals1->data, - bcm->initvals1->size / sizeof(struct bcm43xx_initval)); + if (phy->initvals1) { + err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)phy->initvals1->data, + phy->initvals1->size / sizeof(struct bcm43xx_initval)); if (err) goto out; } @@ -2156,9 +2116,7 @@ static struct pci_device_id bcm43xx_47xx_ids[] = { static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm) { - int res; - unsigned int i; - u32 data; + int err; bcm->irq = bcm->pci_dev->irq; #ifdef CONFIG_BCM947XX @@ -2175,32 +2133,12 @@ static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm) } } #endif - res = request_irq(bcm->irq, bcm43xx_interrupt_handler, + err = request_irq(bcm->irq, bcm43xx_interrupt_handler, IRQF_SHARED, KBUILD_MODNAME, bcm); - if (res) { + if (err) printk(KERN_ERR PFX "Cannot register IRQ%d\n", bcm->irq); - return -ENODEV; - } - bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xffffffff); - bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402); - i = 0; - while (1) { - data = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); - if (data == BCM43xx_IRQ_READY) - break; - i++; - if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) { - printk(KERN_ERR PFX "Card IRQ register not responding. " - "Giving up.\n"); - free_irq(bcm->irq, bcm); - return -ENODEV; - } - udelay(10); - } - // dummy read - bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); - return 0; + return err; } /* Switch to the core used to write the GPIO register. @@ -2298,13 +2236,17 @@ static int bcm43xx_gpio_cleanup(struct bcm43xx_private *bcm) /* http://bcm-specs.sipsolutions.net/EnableMac */ void bcm43xx_mac_enable(struct bcm43xx_private *bcm) { - bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, - bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) - | BCM43xx_SBF_MAC_ENABLED); - bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, BCM43xx_IRQ_READY); - bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ - bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ - bcm43xx_power_saving_ctl_bits(bcm, -1, -1); + bcm->mac_suspended--; + assert(bcm->mac_suspended >= 0); + if (bcm->mac_suspended == 0) { + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) + | BCM43xx_SBF_MAC_ENABLED); + bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, BCM43xx_IRQ_READY); + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ + bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ + bcm43xx_power_saving_ctl_bits(bcm, -1, -1); + } } /* http://bcm-specs.sipsolutions.net/SuspendMAC */ @@ -2313,18 +2255,23 @@ void bcm43xx_mac_suspend(struct bcm43xx_private *bcm) int i; u32 tmp; - bcm43xx_power_saving_ctl_bits(bcm, -1, 1); - bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, - bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) - & ~BCM43xx_SBF_MAC_ENABLED); - bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ - for (i = 100000; i; i--) { - tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); - if (tmp & BCM43xx_IRQ_READY) - return; - udelay(10); + assert(bcm->mac_suspended >= 0); + if (bcm->mac_suspended == 0) { + bcm43xx_power_saving_ctl_bits(bcm, -1, 1); + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) + & ~BCM43xx_SBF_MAC_ENABLED); + bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ + for (i = 10000; i; i--) { + tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); + if (tmp & BCM43xx_IRQ_READY) + goto out; + udelay(1); + } + printkl(KERN_ERR PFX "MAC suspend failed\n"); } - printkl(KERN_ERR PFX "MAC suspend failed\n"); +out: + bcm->mac_suspended++; } void bcm43xx_set_iwmode(struct bcm43xx_private *bcm, @@ -2394,7 +2341,6 @@ static void bcm43xx_chip_cleanup(struct bcm43xx_private *bcm) if (!modparam_noleds) bcm43xx_leds_exit(bcm); bcm43xx_gpio_cleanup(bcm); - free_irq(bcm->irq, bcm); bcm43xx_release_firmware(bcm, 0); } @@ -2406,7 +2352,7 @@ static int bcm43xx_chip_init(struct bcm43xx_private *bcm) struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); int err; - int tmp; + int i, tmp; u32 value32; u16 value16; @@ -2419,13 +2365,26 @@ static int bcm43xx_chip_init(struct bcm43xx_private *bcm) goto out; bcm43xx_upload_microcode(bcm); - err = bcm43xx_initialize_irq(bcm); - if (err) - goto err_release_fw; + bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xFFFFFFFF); + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402); + i = 0; + while (1) { + value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); + if (value32 == BCM43xx_IRQ_READY) + break; + i++; + if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) { + printk(KERN_ERR PFX "IRQ_READY timeout\n"); + err = -ENODEV; + goto err_release_fw; + } + udelay(10); + } + bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ err = bcm43xx_gpio_init(bcm); if (err) - goto err_free_irq; + goto err_release_fw; err = bcm43xx_upload_initvals(bcm); if (err) @@ -2509,8 +2468,6 @@ err_radio_off: bcm43xx_radio_turn_off(bcm); err_gpio_cleanup: bcm43xx_gpio_cleanup(bcm); -err_free_irq: - free_irq(bcm->irq, bcm); err_release_fw: bcm43xx_release_firmware(bcm, 1); goto out; @@ -2550,11 +2507,9 @@ static void bcm43xx_init_struct_phyinfo(struct bcm43xx_phyinfo *phy) { /* Initialize a "phyinfo" structure. The structure is already * zeroed out. + * This is called on insmod time to initialize members. */ - phy->antenna_diversity = 0xFFFF; phy->savedpctlreg = 0xFFFF; - phy->minlowsig[0] = 0xFFFF; - phy->minlowsig[1] = 0xFFFF; spin_lock_init(&phy->lock); } @@ -2562,14 +2517,11 @@ static void bcm43xx_init_struct_radioinfo(struct bcm43xx_radioinfo *radio) { /* Initialize a "radioinfo" structure. The structure is already * zeroed out. + * This is called on insmod time to initialize members. */ radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE; radio->channel = 0xFF; radio->initial_channel = 0xFF; - radio->lofcal = 0xFFFF; - radio->initval = 0xFFFF; - radio->nrssi[0] = -1000; - radio->nrssi[1] = -1000; } static int bcm43xx_probe_cores(struct bcm43xx_private *bcm) @@ -2587,7 +2539,6 @@ static int bcm43xx_probe_cores(struct bcm43xx_private *bcm) * BCM43xx_MAX_80211_CORES); memset(&bcm->core_80211_ext, 0, sizeof(struct bcm43xx_coreinfo_80211) * BCM43xx_MAX_80211_CORES); - bcm->current_80211_core_idx = -1; bcm->nr_80211_available = 0; bcm->current_core = NULL; bcm->active_80211_core = NULL; @@ -2757,6 +2708,7 @@ static int bcm43xx_probe_cores(struct bcm43xx_private *bcm) goto out; } bcm->nr_80211_available++; + core->priv = ext_80211; bcm43xx_init_struct_phyinfo(&ext_80211->phy); bcm43xx_init_struct_radioinfo(&ext_80211->radio); break; @@ -2857,7 +2809,8 @@ static void bcm43xx_wireless_core_cleanup(struct bcm43xx_private *bcm) } /* http://bcm-specs.sipsolutions.net/80211Init */ -static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm) +static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm, + int active_wlcore) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); @@ -2939,19 +2892,26 @@ static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm) if (bcm->current_core->rev >= 5) bcm43xx_write16(bcm, 0x043C, 0x000C); - if (bcm43xx_using_pio(bcm)) - err = bcm43xx_pio_init(bcm); - else - err = bcm43xx_dma_init(bcm); - if (err) - goto err_chip_cleanup; + if (active_wlcore) { + if (bcm43xx_using_pio(bcm)) + err = bcm43xx_pio_init(bcm); + else + err = bcm43xx_dma_init(bcm); + if (err) + goto err_chip_cleanup; + } bcm43xx_write16(bcm, 0x0612, 0x0050); bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0416, 0x0050); bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0414, 0x01F4); - bcm43xx_mac_enable(bcm); - bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate); + if (active_wlcore) { + if (radio->initial_channel != 0xFF) + bcm43xx_radio_selectchannel(bcm, radio->initial_channel, 0); + } + /* Don't enable MAC/IRQ here, as it will race with the IRQ handler. + * We enable it later. + */ bcm->current_core->initialized = 1; out: return err; @@ -3066,11 +3026,6 @@ out: return err; } -static void bcm43xx_softmac_init(struct bcm43xx_private *bcm) -{ - ieee80211softmac_start(bcm->net_dev); -} - static void bcm43xx_periodic_every120sec(struct bcm43xx_private *bcm) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); @@ -3182,39 +3137,40 @@ static void bcm43xx_periodic_work_handler(void *d) /* Periodic work will take a long time, so we want it to * be preemtible. */ - bcm43xx_lock_irqonly(bcm, flags); netif_stop_queue(bcm->net_dev); + synchronize_net(); + spin_lock_irqsave(&bcm->irq_lock, flags); + bcm43xx_mac_suspend(bcm); if (bcm43xx_using_pio(bcm)) bcm43xx_pio_freeze_txqueues(bcm); savedirqs = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); - bcm43xx_unlock_irqonly(bcm, flags); - bcm43xx_lock_noirq(bcm); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_lock(&bcm->mutex); bcm43xx_synchronize_irq(bcm); } else { /* Periodic work should take short time, so we want low * locking overhead. */ - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); } do_periodic_work(bcm); if (badness > BADNESS_LIMIT) { - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&bcm->irq_lock, flags); if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) { tasklet_enable(&bcm->isr_tasklet); bcm43xx_interrupt_enable(bcm, savedirqs); if (bcm43xx_using_pio(bcm)) bcm43xx_pio_thaw_txqueues(bcm); + bcm43xx_mac_enable(bcm); } netif_wake_queue(bcm->net_dev); - mmiowb(); - bcm43xx_unlock_irqonly(bcm, flags); - bcm43xx_unlock_noirq(bcm); - } else { - mmiowb(); - bcm43xx_unlock_irqsafe(bcm, flags); } + mmiowb(); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); } static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm) @@ -3243,9 +3199,9 @@ static int bcm43xx_rng_read(struct hwrng *rng, u32 *data) struct bcm43xx_private *bcm = (struct bcm43xx_private *)rng->priv; unsigned long flags; - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&(bcm)->irq_lock, flags); *data = bcm43xx_read16(bcm, BCM43xx_MMIO_RNG); - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&(bcm)->irq_lock, flags); return (sizeof(u16)); } @@ -3271,139 +3227,322 @@ static int bcm43xx_rng_init(struct bcm43xx_private *bcm) return err; } -/* This is the opposite of bcm43xx_init_board() */ -static void bcm43xx_free_board(struct bcm43xx_private *bcm) +static int bcm43xx_shutdown_all_wireless_cores(struct bcm43xx_private *bcm) { + int ret = 0; int i, err; + struct bcm43xx_coreinfo *core; - bcm43xx_lock_noirq(bcm); + bcm43xx_set_status(bcm, BCM43xx_STAT_SHUTTINGDOWN); + for (i = 0; i < bcm->nr_80211_available; i++) { + core = &(bcm->core_80211[i]); + assert(core->available); + if (!core->initialized) + continue; + err = bcm43xx_switch_core(bcm, core); + if (err) { + dprintk(KERN_ERR PFX "shutdown_all_wireless_cores " + "switch_core failed (%d)\n", err); + ret = err; + continue; + } + bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); + bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ + bcm43xx_wireless_core_cleanup(bcm); + if (core == bcm->active_80211_core) + bcm->active_80211_core = NULL; + } + free_irq(bcm->irq, bcm); + bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT); + + return ret; +} + +/* This is the opposite of bcm43xx_init_board() */ +static void bcm43xx_free_board(struct bcm43xx_private *bcm) +{ bcm43xx_sysfs_unregister(bcm); bcm43xx_periodic_tasks_delete(bcm); - bcm43xx_set_status(bcm, BCM43xx_STAT_SHUTTINGDOWN); + mutex_lock(&(bcm)->mutex); + bcm43xx_shutdown_all_wireless_cores(bcm); + bcm43xx_pctl_set_crystal(bcm, 0); + mutex_unlock(&(bcm)->mutex); +} + +static void prepare_phydata_for_init(struct bcm43xx_phyinfo *phy) +{ + phy->antenna_diversity = 0xFFFF; + memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig)); + memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos)); + + /* Flags */ + phy->calibrated = 0; + phy->is_locked = 0; + + if (phy->_lo_pairs) { + memset(phy->_lo_pairs, 0, + sizeof(struct bcm43xx_lopair) * BCM43xx_LO_COUNT); + } + memset(phy->loopback_gain, 0, sizeof(phy->loopback_gain)); +} + +static void prepare_radiodata_for_init(struct bcm43xx_private *bcm, + struct bcm43xx_radioinfo *radio) +{ + int i; + + /* Set default attenuation values. */ + radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm); + radio->radio_atten = bcm43xx_default_radio_attenuation(bcm); + radio->txctl1 = bcm43xx_default_txctl1(bcm); + radio->txctl2 = 0xFFFF; + radio->txpwr_offset = 0; + + /* NRSSI */ + radio->nrssislope = 0; + for (i = 0; i < ARRAY_SIZE(radio->nrssi); i++) + radio->nrssi[i] = -1000; + for (i = 0; i < ARRAY_SIZE(radio->nrssi_lt); i++) + radio->nrssi_lt[i] = i; + + radio->lofcal = 0xFFFF; + radio->initval = 0xFFFF; + + radio->aci_enable = 0; + radio->aci_wlan_automatic = 0; + radio->aci_hw_rssi = 0; +} + +static void prepare_priv_for_init(struct bcm43xx_private *bcm) +{ + int i; + struct bcm43xx_coreinfo *core; + struct bcm43xx_coreinfo_80211 *wlext; - bcm43xx_rng_exit(bcm); + assert(!bcm->active_80211_core); + + bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING); + + /* Flags */ + bcm->was_initialized = 0; + bcm->reg124_set_0x4 = 0; + + /* Stats */ + memset(&bcm->stats, 0, sizeof(bcm->stats)); + + /* Wireless core data */ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) { - if (!bcm->core_80211[i].available) - continue; - if (!bcm->core_80211[i].initialized) + core = &(bcm->core_80211[i]); + wlext = core->priv; + + if (!core->available) continue; + assert(wlext == &(bcm->core_80211_ext[i])); - err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]); - assert(err == 0); - bcm43xx_wireless_core_cleanup(bcm); + prepare_phydata_for_init(&wlext->phy); + prepare_radiodata_for_init(bcm, &wlext->radio); } - bcm43xx_pctl_set_crystal(bcm, 0); + /* IRQ related flags */ + bcm->irq_reason = 0; + memset(bcm->dma_reason, 0, sizeof(bcm->dma_reason)); + bcm->irq_savedstate = BCM43xx_IRQ_INITIAL; - bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT); - bcm43xx_unlock_noirq(bcm); + /* Noise calculation context */ + memset(&bcm->noisecalc, 0, sizeof(bcm->noisecalc)); + + /* Periodic work context */ + bcm->periodic_state = 0; } -static int bcm43xx_init_board(struct bcm43xx_private *bcm) +static int wireless_core_up(struct bcm43xx_private *bcm, + int active_wlcore) +{ + int err; + + if (!bcm43xx_core_enabled(bcm)) + bcm43xx_wireless_core_reset(bcm, 1); + if (!active_wlcore) + bcm43xx_wireless_core_mark_inactive(bcm); + err = bcm43xx_wireless_core_init(bcm, active_wlcore); + if (err) + goto out; + if (!active_wlcore) + bcm43xx_radio_turn_off(bcm); +out: + return err; +} + +/* Select and enable the "to be used" wireless core. + * Locking: bcm->mutex must be aquired before calling this. + * bcm->irq_lock must not be aquired. + */ +int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm, + int phytype) { int i, err; - int connect_phy; + struct bcm43xx_coreinfo *active_core = NULL; + struct bcm43xx_coreinfo_80211 *active_wlext = NULL; + struct bcm43xx_coreinfo *core; + struct bcm43xx_coreinfo_80211 *wlext; + int adjust_active_sbtmstatelow = 0; might_sleep(); - bcm43xx_lock_noirq(bcm); - bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING); + if (phytype < 0) { + /* If no phytype is requested, select the first core. */ + assert(bcm->core_80211[0].available); + wlext = bcm->core_80211[0].priv; + phytype = wlext->phy.type; + } + /* Find the requested core. */ + for (i = 0; i < bcm->nr_80211_available; i++) { + core = &(bcm->core_80211[i]); + wlext = core->priv; + if (wlext->phy.type == phytype) { + active_core = core; + active_wlext = wlext; + break; + } + } + if (!active_core) + return -ESRCH; /* No such PHYTYPE on this board. */ + + if (bcm->active_80211_core) { + /* We already selected a wl core in the past. + * So first clean up everything. + */ + dprintk(KERN_INFO PFX "select_wireless_core: cleanup\n"); + ieee80211softmac_stop(bcm->net_dev); + bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED); + err = bcm43xx_disable_interrupts_sync(bcm); + assert(!err); + tasklet_enable(&bcm->isr_tasklet); + err = bcm43xx_shutdown_all_wireless_cores(bcm); + if (err) + goto error; + /* Ok, everything down, continue to re-initialize. */ + bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING); + } + + /* Reset all data structures. */ + prepare_priv_for_init(bcm); - err = bcm43xx_pctl_set_crystal(bcm, 1); - if (err) - goto out; - err = bcm43xx_pctl_init(bcm); - if (err) - goto err_crystal_off; err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_FAST); if (err) - goto err_crystal_off; + goto error; - tasklet_enable(&bcm->isr_tasklet); + /* Mark all unused cores "inactive". */ for (i = 0; i < bcm->nr_80211_available; i++) { - err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]); - assert(err != -ENODEV); - if (err) - goto err_80211_unwind; + core = &(bcm->core_80211[i]); + wlext = core->priv; - /* Enable the selected wireless core. - * Connect PHY only on the first core. - */ - if (!bcm43xx_core_enabled(bcm)) { - if (bcm->nr_80211_available == 1) { - connect_phy = bcm43xx_current_phy(bcm)->connected; - } else { - if (i == 0) - connect_phy = 1; - else - connect_phy = 0; - } - bcm43xx_wireless_core_reset(bcm, connect_phy); + if (core == active_core) + continue; + err = bcm43xx_switch_core(bcm, core); + if (err) { + dprintk(KERN_ERR PFX "Could not switch to inactive " + "802.11 core (%d)\n", err); + goto error; } + err = wireless_core_up(bcm, 0); + if (err) { + dprintk(KERN_ERR PFX "core_up for inactive 802.11 core " + "failed (%d)\n", err); + goto error; + } + adjust_active_sbtmstatelow = 1; + } - if (i != 0) - bcm43xx_wireless_core_mark_inactive(bcm, &bcm->core_80211[0]); - - err = bcm43xx_wireless_core_init(bcm); - if (err) - goto err_80211_unwind; + /* Now initialize the active 802.11 core. */ + err = bcm43xx_switch_core(bcm, active_core); + if (err) { + dprintk(KERN_ERR PFX "Could not switch to active " + "802.11 core (%d)\n", err); + goto error; + } + if (adjust_active_sbtmstatelow && + active_wlext->phy.type == BCM43xx_PHYTYPE_G) { + u32 sbtmstatelow; - if (i != 0) { - bcm43xx_mac_suspend(bcm); - bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); - bcm43xx_radio_turn_off(bcm); - } + sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); + sbtmstatelow |= 0x20000000; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); } - bcm->active_80211_core = &bcm->core_80211[0]; - if (bcm->nr_80211_available >= 2) { - bcm43xx_switch_core(bcm, &bcm->core_80211[0]); - bcm43xx_mac_enable(bcm); + err = wireless_core_up(bcm, 1); + if (err) { + dprintk(KERN_ERR PFX "core_up for active 802.11 core " + "failed (%d)\n", err); + goto error; } - err = bcm43xx_rng_init(bcm); + err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC); if (err) - goto err_80211_unwind; + goto error; + bcm->active_80211_core = active_core; + bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC); bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_SELF, (u8 *)(bcm->net_dev->dev_addr)); - dprintk(KERN_INFO PFX "80211 cores initialized\n"); bcm43xx_security_init(bcm); - bcm43xx_softmac_init(bcm); + ieee80211softmac_start(bcm->net_dev); - bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC); + /* Let's go! Be careful after enabling the IRQs. + * Don't switch cores, for example. + */ + bcm43xx_mac_enable(bcm); + bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED); + err = bcm43xx_initialize_irq(bcm); + if (err) + goto error; + bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate); - if (bcm43xx_current_radio(bcm)->initial_channel != 0xFF) { - bcm43xx_mac_suspend(bcm); - bcm43xx_radio_selectchannel(bcm, bcm43xx_current_radio(bcm)->initial_channel, 0); - bcm43xx_mac_enable(bcm); - } + dprintk(KERN_INFO PFX "Selected 802.11 core (phytype %d)\n", + active_wlext->phy.type); - /* Initialization of the board is done. Flag it as such. */ - bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED); + return 0; + +error: + bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT); + bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW); + return err; +} + +static int bcm43xx_init_board(struct bcm43xx_private *bcm) +{ + int err; + + mutex_lock(&(bcm)->mutex); + + tasklet_enable(&bcm->isr_tasklet); + err = bcm43xx_pctl_set_crystal(bcm, 1); + if (err) + goto err_tasklet; + err = bcm43xx_pctl_init(bcm); + if (err) + goto err_crystal_off; + err = bcm43xx_select_wireless_core(bcm, -1); + if (err) + goto err_crystal_off; bcm43xx_periodic_tasks_setup(bcm); - bcm43xx_sysfs_register(bcm); - //FIXME: check for bcm43xx_sysfs_register failure. This function is a bit messy regarding unwinding, though... + err = bcm43xx_sysfs_register(bcm); + if (err) + goto err_wlshutdown; /*FIXME: This should be handled by softmac instead. */ schedule_work(&bcm->softmac->associnfo.work); - assert(err == 0); out: - bcm43xx_unlock_noirq(bcm); + mutex_unlock(&(bcm)->mutex); return err; -err_80211_unwind: - tasklet_disable(&bcm->isr_tasklet); - /* unwind all 80211 initialization */ - for (i = 0; i < bcm->nr_80211_available; i++) { - if (!bcm->core_80211[i].initialized) - continue; - bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); - bcm43xx_wireless_core_cleanup(bcm); - } +err_wlshutdown: + bcm43xx_shutdown_all_wireless_cores(bcm); err_crystal_off: bcm43xx_pctl_set_crystal(bcm, 0); +err_tasklet: + tasklet_disable(&bcm->isr_tasklet); goto out; } @@ -3647,7 +3786,8 @@ static void bcm43xx_ieee80211_set_chan(struct net_device *net_dev, struct bcm43xx_radioinfo *radio; unsigned long flags; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { bcm43xx_mac_suspend(bcm); bcm43xx_radio_selectchannel(bcm, channel, 0); @@ -3656,7 +3796,8 @@ static void bcm43xx_ieee80211_set_chan(struct net_device *net_dev, radio = bcm43xx_current_radio(bcm); radio->initial_channel = channel; } - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); } /* set_security() callback in struct ieee80211_device */ @@ -3670,7 +3811,8 @@ static void bcm43xx_ieee80211_set_security(struct net_device *net_dev, dprintk(KERN_INFO PFX "set security called"); - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); for (keyidx = 0; keyidx<WEP_KEYS; keyidx++) if (sec->flags & (1<<keyidx)) { @@ -3739,7 +3881,8 @@ static void bcm43xx_ieee80211_set_security(struct net_device *net_dev, } else bcm43xx_clear_keys(bcm); } - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); } /* hard_start_xmit() callback in struct ieee80211_device */ @@ -3751,10 +3894,10 @@ static int bcm43xx_ieee80211_hard_start_xmit(struct ieee80211_txb *txb, int err = -ENODEV; unsigned long flags; - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&bcm->irq_lock, flags); if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) err = bcm43xx_tx(bcm, txb); - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); return err; } @@ -3769,9 +3912,9 @@ static void bcm43xx_net_tx_timeout(struct net_device *net_dev) struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); unsigned long flags; - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&bcm->irq_lock, flags); bcm43xx_controller_restart(bcm, "TX timeout"); - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); } #ifdef CONFIG_NET_POLL_CONTROLLER @@ -3781,7 +3924,8 @@ static void bcm43xx_net_poll_controller(struct net_device *net_dev) unsigned long flags; local_irq_save(flags); - bcm43xx_interrupt_handler(bcm->irq, bcm, NULL); + if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) + bcm43xx_interrupt_handler(bcm->irq, bcm, NULL); local_irq_restore(flags); } #endif /* CONFIG_NET_POLL_CONTROLLER */ @@ -3799,7 +3943,7 @@ static int bcm43xx_net_stop(struct net_device *net_dev) int err; ieee80211softmac_stop(net_dev); - err = bcm43xx_disable_interrupts_sync(bcm, NULL); + err = bcm43xx_disable_interrupts_sync(bcm); assert(!err); bcm43xx_free_board(bcm); @@ -3818,10 +3962,12 @@ static int bcm43xx_init_private(struct bcm43xx_private *bcm, bcm->softmac->set_channel = bcm43xx_ieee80211_set_chan; bcm->irq_savedstate = BCM43xx_IRQ_INITIAL; + bcm->mac_suspended = 1; bcm->pci_dev = pci_dev; bcm->net_dev = net_dev; bcm->bad_frames_preempt = modparam_bad_frames_preempt; spin_lock_init(&bcm->irq_lock); + spin_lock_init(&bcm->leds_lock); mutex_init(&bcm->mutex); tasklet_init(&bcm->isr_tasklet, (void (*)(unsigned long))bcm43xx_interrupt_tasklet, @@ -3940,7 +4086,6 @@ static void __devexit bcm43xx_remove_one(struct pci_dev *pdev) bcm43xx_debugfs_remove_device(bcm); unregister_netdev(net_dev); bcm43xx_detach_board(bcm); - assert(bcm->ucode == NULL); free_ieee80211softmac(net_dev); } @@ -3950,47 +4095,25 @@ static void __devexit bcm43xx_remove_one(struct pci_dev *pdev) static void bcm43xx_chip_reset(void *_bcm) { struct bcm43xx_private *bcm = _bcm; - struct net_device *net_dev = bcm->net_dev; - struct pci_dev *pci_dev = bcm->pci_dev; + struct bcm43xx_phyinfo *phy; int err; - int was_initialized = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); - netif_stop_queue(bcm->net_dev); - tasklet_disable(&bcm->isr_tasklet); + mutex_lock(&(bcm)->mutex); + phy = bcm43xx_current_phy(bcm); + err = bcm43xx_select_wireless_core(bcm, phy->type); + mutex_unlock(&(bcm)->mutex); - bcm->firmware_norelease = 1; - if (was_initialized) - bcm43xx_free_board(bcm); - bcm->firmware_norelease = 0; - bcm43xx_detach_board(bcm); - err = bcm43xx_init_private(bcm, net_dev, pci_dev); - if (err) - goto failure; - err = bcm43xx_attach_board(bcm); - if (err) - goto failure; - if (was_initialized) { - err = bcm43xx_init_board(bcm); - if (err) - goto failure; - } - netif_wake_queue(bcm->net_dev); - printk(KERN_INFO PFX "Controller restarted\n"); - - return; -failure: - printk(KERN_ERR PFX "Controller restart failed\n"); + printk(KERN_ERR PFX "Controller restart%s\n", + (err == 0) ? "ed" : " failed"); } /* Hard-reset the chip. * This can be called from interrupt or process context. - * Make sure to _not_ re-enable device interrupts after this has been called. -*/ + */ void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason) { + assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); bcm43xx_set_status(bcm, BCM43xx_STAT_RESTARTING); - bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); - bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ printk(KERN_ERR PFX "Controller RESET (%s) ...\n", reason); INIT_WORK(&bcm->restart_work, bcm43xx_chip_reset, bcm); schedule_work(&bcm->restart_work); @@ -4002,21 +4125,16 @@ static int bcm43xx_suspend(struct pci_dev *pdev, pm_message_t state) { struct net_device *net_dev = pci_get_drvdata(pdev); struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); - unsigned long flags; - int try_to_shutdown = 0, err; + int err; dprintk(KERN_INFO PFX "Suspending...\n"); - bcm43xx_lock_irqsafe(bcm, flags); - bcm->was_initialized = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); - if (bcm->was_initialized) - try_to_shutdown = 1; - bcm43xx_unlock_irqsafe(bcm, flags); - netif_device_detach(net_dev); - if (try_to_shutdown) { + bcm->was_initialized = 0; + if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { + bcm->was_initialized = 1; ieee80211softmac_stop(net_dev); - err = bcm43xx_disable_interrupts_sync(bcm, &bcm->irq_savedstate); + err = bcm43xx_disable_interrupts_sync(bcm); if (unlikely(err)) { dprintk(KERN_ERR PFX "Suspend failed.\n"); return -EAGAIN; @@ -4049,17 +4167,14 @@ static int bcm43xx_resume(struct pci_dev *pdev) pci_restore_state(pdev); bcm43xx_chipset_attach(bcm); - if (bcm->was_initialized) { - bcm->irq_savedstate = BCM43xx_IRQ_INITIAL; + if (bcm->was_initialized) err = bcm43xx_init_board(bcm); - } if (err) { printk(KERN_ERR PFX "Resume failed!\n"); return err; } - netif_device_attach(net_dev); - + dprintk(KERN_INFO PFX "Device resumed.\n"); return 0; diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.h b/drivers/net/wireless/bcm43xx/bcm43xx_main.h index 116493671f8..505c86e2007 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.h @@ -133,6 +133,9 @@ void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm); int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core); +int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm, + int phytype); + void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy); void bcm43xx_mac_suspend(struct bcm43xx_private *bcm); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c index f8200deecc8..eafd0f66268 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c @@ -81,6 +81,16 @@ static const s8 bcm43xx_tssi2dbm_g_table[] = { static void bcm43xx_phy_initg(struct bcm43xx_private *bcm); +static inline +void bcm43xx_voluntary_preempt(void) +{ + assert(!in_atomic() && !in_irq() && + !in_interrupt() && !irqs_disabled()); +#ifndef CONFIG_PREEMPT + cond_resched(); +#endif /* CONFIG_PREEMPT */ +} + void bcm43xx_raw_phy_lock(struct bcm43xx_private *bcm) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); @@ -133,22 +143,14 @@ void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val) void bcm43xx_phy_calibrate(struct bcm43xx_private *bcm) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); - unsigned long flags; bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* Dummy read. */ if (phy->calibrated) return; if (phy->type == BCM43xx_PHYTYPE_G && phy->rev == 1) { - /* We do not want to be preempted while calibrating - * the hardware. - */ - local_irq_save(flags); - bcm43xx_wireless_core_reset(bcm, 0); bcm43xx_phy_initg(bcm); bcm43xx_wireless_core_reset(bcm, 1); - - local_irq_restore(flags); } phy->calibrated = 1; } @@ -1299,7 +1301,9 @@ static u16 bcm43xx_phy_lo_b_r15_loop(struct bcm43xx_private *bcm) { int i; u16 ret = 0; + unsigned long flags; + local_irq_save(flags); for (i = 0; i < 10; i++){ bcm43xx_phy_write(bcm, 0x0015, 0xAFA0); udelay(1); @@ -1309,6 +1313,8 @@ static u16 bcm43xx_phy_lo_b_r15_loop(struct bcm43xx_private *bcm) udelay(40); ret += bcm43xx_phy_read(bcm, 0x002C); } + local_irq_restore(flags); + bcm43xx_voluntary_preempt(); return ret; } @@ -1435,6 +1441,7 @@ u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control) } ret = bcm43xx_phy_read(bcm, 0x002D); local_irq_restore(flags); + bcm43xx_voluntary_preempt(); return ret; } @@ -1760,6 +1767,7 @@ void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm) bcm43xx_radio_write16(bcm, 0x43, i); bcm43xx_radio_write16(bcm, 0x52, radio->txctl2); udelay(10); + bcm43xx_voluntary_preempt(); bcm43xx_phy_set_baseband_attenuation(bcm, j * 2); @@ -1803,6 +1811,7 @@ void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm) radio->txctl2 | (3/*txctl1*/ << 4));//FIXME: shouldn't txctl1 be zero here and 3 in the loop above? udelay(10); + bcm43xx_voluntary_preempt(); bcm43xx_phy_set_baseband_attenuation(bcm, j * 2); @@ -1824,6 +1833,7 @@ void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm) bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA2); udelay(2); bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA3); + bcm43xx_voluntary_preempt(); } else bcm43xx_phy_write(bcm, 0x0015, r27 | 0xEFA0); bcm43xx_phy_lo_adjust(bcm, is_initializing); @@ -2188,12 +2198,6 @@ int bcm43xx_phy_init(struct bcm43xx_private *bcm) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); int err = -ENODEV; - unsigned long flags; - - /* We do not want to be preempted while calibrating - * the hardware. - */ - local_irq_save(flags); switch (phy->type) { case BCM43xx_PHYTYPE_A: @@ -2227,7 +2231,6 @@ int bcm43xx_phy_init(struct bcm43xx_private *bcm) err = 0; break; } - local_irq_restore(flags); if (err) printk(KERN_WARNING PFX "Unknown PHYTYPE found!\n"); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c index 574085c4615..c60c1743ea0 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c @@ -262,7 +262,7 @@ static void tx_tasklet(unsigned long d) int err; u16 txctl; - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&bcm->irq_lock, flags); if (queue->tx_frozen) goto out_unlock; @@ -300,7 +300,7 @@ static void tx_tasklet(unsigned long d) continue; } out_unlock: - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); } static void setup_txqueues(struct bcm43xx_pioqueue *queue) diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c index 6a23bdc7541..ece335178f6 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c @@ -120,12 +120,14 @@ static ssize_t bcm43xx_attr_sprom_show(struct device *dev, GFP_KERNEL); if (!sprom) return -ENOMEM; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); err = bcm43xx_sprom_read(bcm, sprom); if (!err) err = sprom2hex(sprom, buf, PAGE_SIZE); mmiowb(); - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); kfree(sprom); return err; @@ -150,10 +152,14 @@ static ssize_t bcm43xx_attr_sprom_store(struct device *dev, err = hex2sprom(sprom, buf, count); if (err) goto out_kfree; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); + spin_lock(&bcm->leds_lock); err = bcm43xx_sprom_write(bcm, sprom); mmiowb(); - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock(&bcm->leds_lock); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); out_kfree: kfree(sprom); @@ -176,7 +182,7 @@ static ssize_t bcm43xx_attr_interfmode_show(struct device *dev, if (!capable(CAP_NET_ADMIN)) return -EPERM; - bcm43xx_lock_noirq(bcm); + mutex_lock(&bcm->mutex); switch (bcm43xx_current_radio(bcm)->interfmode) { case BCM43xx_RADIO_INTERFMODE_NONE: @@ -193,7 +199,7 @@ static ssize_t bcm43xx_attr_interfmode_show(struct device *dev, } err = 0; - bcm43xx_unlock_noirq(bcm); + mutex_unlock(&bcm->mutex); return err ? err : count; @@ -229,7 +235,8 @@ static ssize_t bcm43xx_attr_interfmode_store(struct device *dev, return -EINVAL; } - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); err = bcm43xx_radio_set_interference_mitigation(bcm, mode); if (err) { @@ -237,7 +244,8 @@ static ssize_t bcm43xx_attr_interfmode_store(struct device *dev, "supported by device\n"); } mmiowb(); - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); return err ? err : count; } @@ -257,7 +265,7 @@ static ssize_t bcm43xx_attr_preamble_show(struct device *dev, if (!capable(CAP_NET_ADMIN)) return -EPERM; - bcm43xx_lock_noirq(bcm); + mutex_lock(&bcm->mutex); if (bcm->short_preamble) count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble enabled)\n"); @@ -265,7 +273,7 @@ static ssize_t bcm43xx_attr_preamble_show(struct device *dev, count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble disabled)\n"); err = 0; - bcm43xx_unlock_noirq(bcm); + mutex_unlock(&bcm->mutex); return err ? err : count; } @@ -285,12 +293,14 @@ static ssize_t bcm43xx_attr_preamble_store(struct device *dev, value = get_boolean(buf, count); if (value < 0) return value; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); bcm->short_preamble = !!value; err = 0; - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); return err ? err : count; } @@ -299,6 +309,70 @@ static DEVICE_ATTR(shortpreamble, 0644, bcm43xx_attr_preamble_show, bcm43xx_attr_preamble_store); +static ssize_t bcm43xx_attr_phymode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bcm43xx_private *bcm = dev_to_bcm(dev); + int phytype; + int err = -EINVAL; + + if (count < 1) + goto out; + switch (buf[0]) { + case 'a': case 'A': + phytype = BCM43xx_PHYTYPE_A; + break; + case 'b': case 'B': + phytype = BCM43xx_PHYTYPE_B; + break; + case 'g': case 'G': + phytype = BCM43xx_PHYTYPE_G; + break; + default: + goto out; + } + + mutex_lock(&(bcm)->mutex); + err = bcm43xx_select_wireless_core(bcm, phytype); + mutex_unlock(&(bcm)->mutex); + if (err == -ESRCH) + err = -ENODEV; + +out: + return err ? err : count; +} + +static ssize_t bcm43xx_attr_phymode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bcm43xx_private *bcm = dev_to_bcm(dev); + ssize_t count = 0; + + mutex_lock(&(bcm)->mutex); + switch (bcm43xx_current_phy(bcm)->type) { + case BCM43xx_PHYTYPE_A: + snprintf(buf, PAGE_SIZE, "A"); + break; + case BCM43xx_PHYTYPE_B: + snprintf(buf, PAGE_SIZE, "B"); + break; + case BCM43xx_PHYTYPE_G: + snprintf(buf, PAGE_SIZE, "G"); + break; + default: + assert(0); + } + mutex_unlock(&(bcm)->mutex); + + return count; +} + +static DEVICE_ATTR(phymode, 0644, + bcm43xx_attr_phymode_show, + bcm43xx_attr_phymode_store); + int bcm43xx_sysfs_register(struct bcm43xx_private *bcm) { struct device *dev = &bcm->pci_dev->dev; @@ -315,9 +389,14 @@ int bcm43xx_sysfs_register(struct bcm43xx_private *bcm) err = device_create_file(dev, &dev_attr_shortpreamble); if (err) goto err_remove_interfmode; + err = device_create_file(dev, &dev_attr_phymode); + if (err) + goto err_remove_shortpreamble; out: return err; +err_remove_shortpreamble: + device_remove_file(dev, &dev_attr_shortpreamble); err_remove_interfmode: device_remove_file(dev, &dev_attr_interference); err_remove_sprom: @@ -329,6 +408,7 @@ void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm) { struct device *dev = &bcm->pci_dev->dev; + device_remove_file(dev, &dev_attr_phymode); device_remove_file(dev, &dev_attr_shortpreamble); device_remove_file(dev, &dev_attr_interference); device_remove_file(dev, &dev_attr_sprom); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c index 5c36e29efff..1d3a3aaf96e 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c @@ -47,6 +47,8 @@ #define BCM43xx_WX_VERSION 18 #define MAX_WX_STRING 80 +/* FIXME: the next line is a guess as to what the maximum RSSI value might be */ +#define RX_RSSI_MAX 60 static int bcm43xx_wx_get_name(struct net_device *net_dev, @@ -56,12 +58,11 @@ static int bcm43xx_wx_get_name(struct net_device *net_dev, { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); int i; - unsigned long flags; struct bcm43xx_phyinfo *phy; char suffix[7] = { 0 }; int have_a = 0, have_b = 0, have_g = 0; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); for (i = 0; i < bcm->nr_80211_available; i++) { phy = &(bcm->core_80211_ext[i].phy); switch (phy->type) { @@ -77,7 +78,7 @@ static int bcm43xx_wx_get_name(struct net_device *net_dev, assert(0); } } - bcm43xx_unlock_irqsafe(bcm, flags); + mutex_unlock(&bcm->mutex); i = 0; if (have_a) { @@ -111,7 +112,9 @@ static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev, int freq; int err = -EINVAL; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); + if ((data->freq.m >= 0) && (data->freq.m <= 1000)) { channel = data->freq.m; freq = bcm43xx_channel_to_freq(bcm, channel); @@ -131,7 +134,8 @@ static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev, err = 0; } out_unlock: - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); return err; } @@ -143,11 +147,10 @@ static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev, { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); struct bcm43xx_radioinfo *radio; - unsigned long flags; int err = -ENODEV; u16 channel; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); radio = bcm43xx_current_radio(bcm); channel = radio->channel; if (channel == 0xFF) { @@ -162,7 +165,7 @@ static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev, err = 0; out_unlock: - bcm43xx_unlock_irqsafe(bcm, flags); + mutex_unlock(&bcm->mutex); return err; } @@ -180,13 +183,15 @@ static int bcm43xx_wx_set_mode(struct net_device *net_dev, if (mode == IW_MODE_AUTO) mode = BCM43xx_INITIAL_IWMODE; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { if (bcm->ieee->iw_mode != mode) bcm43xx_set_iwmode(bcm, mode); } else bcm->ieee->iw_mode = mode; - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); return 0; } @@ -197,11 +202,10 @@ static int bcm43xx_wx_get_mode(struct net_device *net_dev, char *extra) { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); - unsigned long flags; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); data->mode = bcm->ieee->iw_mode; - bcm43xx_unlock_irqsafe(bcm, flags); + mutex_unlock(&bcm->mutex); return 0; } @@ -214,7 +218,6 @@ static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); struct iw_range *range = (struct iw_range *)extra; const struct ieee80211_geo *geo; - unsigned long flags; int i, j; struct bcm43xx_phyinfo *phy; @@ -226,15 +229,14 @@ static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, range->throughput = 27 * 1000 * 1000; range->max_qual.qual = 100; - /* TODO: Real max RSSI */ - range->max_qual.level = 3; - range->max_qual.noise = 100; - range->max_qual.updated = 7; + range->max_qual.level = 152; /* set floor at -104 dBm (152 - 256) */ + range->max_qual.noise = 152; + range->max_qual.updated = IW_QUAL_ALL_UPDATED; - range->avg_qual.qual = 70; - range->avg_qual.level = 2; - range->avg_qual.noise = 40; - range->avg_qual.updated = 7; + range->avg_qual.qual = 50; + range->avg_qual.level = 0; + range->avg_qual.noise = 0; + range->avg_qual.updated = IW_QUAL_ALL_UPDATED; range->min_rts = BCM43xx_MIN_RTS_THRESHOLD; range->max_rts = BCM43xx_MAX_RTS_THRESHOLD; @@ -254,7 +256,7 @@ static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); phy = bcm43xx_current_phy(bcm); range->num_bitrates = 0; @@ -301,7 +303,7 @@ static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, } range->num_frequency = j; - bcm43xx_unlock_irqsafe(bcm, flags); + mutex_unlock(&bcm->mutex); return 0; } @@ -314,11 +316,11 @@ static int bcm43xx_wx_set_nick(struct net_device *net_dev, struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); size_t len; - bcm43xx_lock_noirq(bcm); + mutex_lock(&bcm->mutex); len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE); memcpy(bcm->nick, extra, len); bcm->nick[len] = '\0'; - bcm43xx_unlock_noirq(bcm); + mutex_unlock(&bcm->mutex); return 0; } @@ -331,12 +333,12 @@ static int bcm43xx_wx_get_nick(struct net_device *net_dev, struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); size_t len; - bcm43xx_lock_noirq(bcm); + mutex_lock(&bcm->mutex); len = strlen(bcm->nick) + 1; memcpy(extra, bcm->nick, len); data->data.length = (__u16)len; data->data.flags = 1; - bcm43xx_unlock_noirq(bcm); + mutex_unlock(&bcm->mutex); return 0; } @@ -350,7 +352,8 @@ static int bcm43xx_wx_set_rts(struct net_device *net_dev, unsigned long flags; int err = -EINVAL; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); if (data->rts.disabled) { bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD; err = 0; @@ -361,7 +364,8 @@ static int bcm43xx_wx_set_rts(struct net_device *net_dev, err = 0; } } - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); return err; } @@ -372,13 +376,12 @@ static int bcm43xx_wx_get_rts(struct net_device *net_dev, char *extra) { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); - unsigned long flags; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); data->rts.value = bcm->rts_threshold; data->rts.fixed = 0; data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD); - bcm43xx_unlock_irqsafe(bcm, flags); + mutex_unlock(&bcm->mutex); return 0; } @@ -392,7 +395,8 @@ static int bcm43xx_wx_set_frag(struct net_device *net_dev, unsigned long flags; int err = -EINVAL; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); if (data->frag.disabled) { bcm->ieee->fts = MAX_FRAG_THRESHOLD; err = 0; @@ -403,7 +407,8 @@ static int bcm43xx_wx_set_frag(struct net_device *net_dev, err = 0; } } - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); return err; } @@ -414,13 +419,12 @@ static int bcm43xx_wx_get_frag(struct net_device *net_dev, char *extra) { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); - unsigned long flags; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); data->frag.value = bcm->ieee->fts; data->frag.fixed = 0; data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD); - bcm43xx_unlock_irqsafe(bcm, flags); + mutex_unlock(&bcm->mutex); return 0; } @@ -442,7 +446,8 @@ static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev, return -EOPNOTSUPP; } - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) goto out_unlock; radio = bcm43xx_current_radio(bcm); @@ -466,7 +471,8 @@ static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev, err = 0; out_unlock: - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); return err; } @@ -478,10 +484,9 @@ static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev, { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); struct bcm43xx_radioinfo *radio; - unsigned long flags; int err = -ENODEV; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) goto out_unlock; radio = bcm43xx_current_radio(bcm); @@ -493,7 +498,7 @@ static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev, err = 0; out_unlock: - bcm43xx_unlock_irqsafe(bcm, flags); + mutex_unlock(&bcm->mutex); return err; } @@ -580,7 +585,8 @@ static int bcm43xx_wx_set_interfmode(struct net_device *net_dev, return -EINVAL; } - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { err = bcm43xx_radio_set_interference_mitigation(bcm, mode); if (err) { @@ -595,7 +601,8 @@ static int bcm43xx_wx_set_interfmode(struct net_device *net_dev, } else bcm43xx_current_radio(bcm)->interfmode = mode; } - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); return err; } @@ -606,12 +613,11 @@ static int bcm43xx_wx_get_interfmode(struct net_device *net_dev, char *extra) { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); - unsigned long flags; int mode; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); mode = bcm43xx_current_radio(bcm)->interfmode; - bcm43xx_unlock_irqsafe(bcm, flags); + mutex_unlock(&bcm->mutex); switch (mode) { case BCM43xx_RADIO_INTERFMODE_NONE: @@ -641,9 +647,11 @@ static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev, int on; on = *((int *)extra); - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); bcm->short_preamble = !!on; - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); return 0; } @@ -654,12 +662,11 @@ static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev, char *extra) { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); - unsigned long flags; int on; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); on = bcm->short_preamble; - bcm43xx_unlock_irqsafe(bcm, flags); + mutex_unlock(&bcm->mutex); if (on) strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING); @@ -681,11 +688,13 @@ static int bcm43xx_wx_set_swencryption(struct net_device *net_dev, on = *((int *)extra); - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); bcm->ieee->host_encrypt = !!on; bcm->ieee->host_decrypt = !!on; bcm->ieee->host_build_iv = !on; - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); return 0; } @@ -696,12 +705,11 @@ static int bcm43xx_wx_get_swencryption(struct net_device *net_dev, char *extra) { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); - unsigned long flags; int on; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); on = bcm->ieee->host_encrypt; - bcm43xx_unlock_irqsafe(bcm, flags); + mutex_unlock(&bcm->mutex); if (on) strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING); @@ -764,11 +772,13 @@ static int bcm43xx_wx_sprom_read(struct net_device *net_dev, if (!sprom) goto out; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); err = -ENODEV; if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) err = bcm43xx_sprom_read(bcm, sprom); - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); if (!err) data->data.length = sprom2hex(sprom, extra); kfree(sprom); @@ -809,11 +819,15 @@ static int bcm43xx_wx_sprom_write(struct net_device *net_dev, if (err) goto out_kfree; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); + spin_lock(&bcm->leds_lock); err = -ENODEV; if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) err = bcm43xx_sprom_write(bcm, sprom); - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock(&bcm->leds_lock); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); out_kfree: kfree(sprom); out: @@ -827,6 +841,10 @@ static struct iw_statistics *bcm43xx_get_wireless_stats(struct net_device *net_d struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); struct iw_statistics *wstats; + struct ieee80211_network *network = NULL; + static int tmp_level = 0; + static int tmp_qual = 0; + unsigned long flags; wstats = &bcm->stats.wstats; if (!mac->associated) { @@ -844,16 +862,28 @@ static struct iw_statistics *bcm43xx_get_wireless_stats(struct net_device *net_d wstats->qual.level = 0; wstats->qual.noise = 0; wstats->qual.updated = 7; - wstats->qual.updated |= IW_QUAL_NOISE_INVALID | - IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; + wstats->qual.updated |= IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; return wstats; } /* fill in the real statistics when iface associated */ - wstats->qual.qual = 100; // TODO: get the real signal quality - wstats->qual.level = 3 - bcm->stats.link_quality; + spin_lock_irqsave(&mac->ieee->lock, flags); + list_for_each_entry(network, &mac->ieee->network_list, list) { + if (!memcmp(mac->associnfo.bssid, network->bssid, ETH_ALEN)) { + if (!tmp_level) { /* get initial values */ + tmp_level = network->stats.signal; + tmp_qual = network->stats.rssi; + } else { /* smooth results */ + tmp_level = (15 * tmp_level + network->stats.signal)/16; + tmp_qual = (15 * tmp_qual + network->stats.rssi)/16; + } + break; + } + } + spin_unlock_irqrestore(&mac->ieee->lock, flags); + wstats->qual.level = tmp_level; + wstats->qual.qual = 100 * tmp_qual / RX_RSSI_MAX; wstats->qual.noise = bcm->stats.noise; - wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | - IW_QUAL_NOISE_UPDATED; + wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; wstats->discard.code = bcm->ieee->ieee_stats.rx_discards_undecryptable; wstats->discard.retries = bcm->ieee->ieee_stats.tx_retry_limit_exceeded; wstats->discard.nwid = bcm->ieee->ieee_stats.tx_discards_wrong_sa; diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c index 6dbd855b364..c0efbfe605a 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c @@ -492,16 +492,15 @@ int bcm43xx_rx(struct bcm43xx_private *bcm, memset(&stats, 0, sizeof(stats)); stats.mac_time = le16_to_cpu(rxhdr->mactime); - stats.rssi = bcm43xx_rssi_postprocess(bcm, rxhdr->rssi, is_ofdm, + stats.rssi = rxhdr->rssi; + stats.signal = bcm43xx_rssi_postprocess(bcm, rxhdr->rssi, is_ofdm, !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_2053RSSIADJ), !!(rxflags3 & BCM43xx_RXHDR_FLAGS3_2050RSSIADJ)); - stats.signal = rxhdr->signal_quality; //FIXME //TODO stats.noise = if (is_ofdm) stats.rate = bcm43xx_plcp_get_bitrate_ofdm(plcp); else stats.rate = bcm43xx_plcp_get_bitrate_cck(plcp); -//printk("RX ofdm %d, rate == %u\n", is_ofdm, stats.rate); stats.received_channel = radio->channel; //TODO stats.control = stats.mask = IEEE80211_STATMASK_SIGNAL | diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index b3300ffe4ee..758459e72f3 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c @@ -2667,7 +2667,7 @@ static void ipw_fw_dma_abort(struct ipw_priv *priv) IPW_DEBUG_FW(">> :\n"); - //set the Stop and Abort bit + /* set the Stop and Abort bit */ control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT; ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control); priv->sram_desc.last_cb_index = 0; @@ -3002,8 +3002,6 @@ static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len) if (rc < 0) return rc; -// spin_lock_irqsave(&priv->lock, flags); - for (addr = IPW_SHARED_LOWER_BOUND; addr < IPW_REGISTER_DOMAIN1_END; addr += 4) { ipw_write32(priv, addr, 0); @@ -3097,8 +3095,6 @@ static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len) firmware have problem getting alive resp. */ ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0); -// spin_unlock_irqrestore(&priv->lock, flags); - return rc; } @@ -6387,13 +6383,6 @@ static int ipw_wx_set_genie(struct net_device *dev, (wrqu->data.length && extra == NULL)) return -EINVAL; - //mutex_lock(&priv->mutex); - - //if (!ieee->wpa_enabled) { - // err = -EOPNOTSUPP; - // goto out; - //} - if (wrqu->data.length) { buf = kmalloc(wrqu->data.length, GFP_KERNEL); if (buf == NULL) { @@ -6413,7 +6402,6 @@ static int ipw_wx_set_genie(struct net_device *dev, ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len); out: - //mutex_unlock(&priv->mutex); return err; } @@ -6426,13 +6414,6 @@ static int ipw_wx_get_genie(struct net_device *dev, struct ieee80211_device *ieee = priv->ieee; int err = 0; - //mutex_lock(&priv->mutex); - - //if (!ieee->wpa_enabled) { - // err = -EOPNOTSUPP; - // goto out; - //} - if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) { wrqu->data.length = 0; goto out; @@ -6447,7 +6428,6 @@ static int ipw_wx_get_genie(struct net_device *dev, memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len); out: - //mutex_unlock(&priv->mutex); return err; } @@ -6558,7 +6538,6 @@ static int ipw_wx_set_auth(struct net_device *dev, ieee->ieee802_1x = param->value; break; - //case IW_AUTH_ROAMING_CONTROL: case IW_AUTH_PRIVACY_INVOKED: ieee->privacy_invoked = param->value; break; @@ -6680,7 +6659,7 @@ static int ipw_wx_set_mlme(struct net_device *dev, switch (mlme->cmd) { case IW_MLME_DEAUTH: - // silently ignore + /* silently ignore */ break; case IW_MLME_DISASSOC: @@ -9766,7 +9745,7 @@ static int ipw_wx_set_monitor(struct net_device *dev, return 0; } -#endif // CONFIG_IPW2200_MONITOR +#endif /* CONFIG_IPW2200_MONITOR */ static int ipw_wx_reset(struct net_device *dev, struct iw_request_info *info, @@ -10009,7 +9988,7 @@ static void init_sys_config(struct ipw_sys_config *sys_config) sys_config->dot11g_auto_detection = 0; sys_config->enable_cts_to_self = 0; sys_config->bt_coexist_collision_thr = 0; - sys_config->pass_noise_stats_to_host = 1; //1 -- fix for 256 + sys_config->pass_noise_stats_to_host = 1; /* 1 -- fix for 256 */ sys_config->silence_threshold = 0x1e; } diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 989599ad33e..0c30fe7e8f7 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -35,10 +35,14 @@ #include <net/iw_handler.h> /* New driver API */ +#define KEY_SIZE_WEP104 13 /* 104/128-bit WEP keys */ +#define KEY_SIZE_WEP40 5 /* 40/64-bit WEP keys */ +/* KEY_SIZE_TKIP should match isl_oid.h, struct obj_key.key[] size */ +#define KEY_SIZE_TKIP 32 /* TKIP keys */ -static void prism54_wpa_ie_add(islpci_private *priv, u8 *bssid, +static void prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid, u8 *wpa_ie, size_t wpa_ie_len); -static size_t prism54_wpa_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie); +static size_t prism54_wpa_bss_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie); static int prism54_set_wpa(struct net_device *, struct iw_request_info *, __u32 *, char *); @@ -468,6 +472,9 @@ prism54_get_range(struct net_device *ndev, struct iw_request_info *info, range->event_capa[1] = IW_EVENT_CAPA_K_1; range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVCUSTOM); + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP; + if (islpci_get_state(priv) < PRV_STATE_INIT) return 0; @@ -567,6 +574,8 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, struct iw_event iwe; /* Temporary buffer */ short cap; islpci_private *priv = netdev_priv(ndev); + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; /* The first entry must be the MAC address */ memcpy(iwe.u.ap_addr.sa_data, bss->address, 6); @@ -627,27 +636,13 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); - if (priv->wpa) { - u8 wpa_ie[MAX_WPA_IE_LEN]; - char *buf, *p; - size_t wpa_ie_len; - int i; - - wpa_ie_len = prism54_wpa_ie_get(priv, bss->address, wpa_ie); - if (wpa_ie_len > 0 && - (buf = kmalloc(wpa_ie_len * 2 + 10, GFP_ATOMIC))) { - p = buf; - p += sprintf(p, "wpa_ie="); - for (i = 0; i < wpa_ie_len; i++) { - p += sprintf(p, "%02x", wpa_ie[i]); - } - memset(&iwe, 0, sizeof (iwe)); - iwe.cmd = IWEVCUSTOM; - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, buf); - kfree(buf); - } + /* Add WPA/RSN Information Element, if any */ + wpa_ie_len = prism54_wpa_bss_ie_get(priv, bss->address, wpa_ie); + if (wpa_ie_len > 0) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = min(wpa_ie_len, (size_t)MAX_WPA_IE_LEN); + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, wpa_ie); } return current_ev; } @@ -1051,12 +1046,24 @@ prism54_set_encode(struct net_device *ndev, struct iw_request_info *info, current_index = r.u; /* Verify that the key is not marked as invalid */ if (!(dwrq->flags & IW_ENCODE_NOKEY)) { - key.length = dwrq->length > sizeof (key.key) ? - sizeof (key.key) : dwrq->length; - memcpy(key.key, extra, key.length); - if (key.length == 32) - /* we want WPA-PSK */ + if (dwrq->length > KEY_SIZE_TKIP) { + /* User-provided key data too big */ + return -EINVAL; + } + if (dwrq->length > KEY_SIZE_WEP104) { + /* WPA-PSK TKIP */ key.type = DOT11_PRIV_TKIP; + key.length = KEY_SIZE_TKIP; + } else if (dwrq->length > KEY_SIZE_WEP40) { + /* WEP 104/128 */ + key.length = KEY_SIZE_WEP104; + } else { + /* WEP 40/64 */ + key.length = KEY_SIZE_WEP40; + } + memset(key.key, 0, sizeof (key.key)); + memcpy(key.key, extra, dwrq->length); + if ((index < 0) || (index > 3)) /* no index provided use the current one */ index = current_index; @@ -1210,6 +1217,489 @@ prism54_set_txpower(struct net_device *ndev, struct iw_request_info *info, } } +static int prism54_set_genie(struct net_device *ndev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + int alen, ret = 0; + struct obj_attachment *attach; + + if (data->length > MAX_WPA_IE_LEN || + (data->length && extra == NULL)) + return -EINVAL; + + memcpy(priv->wpa_ie, extra, data->length); + priv->wpa_ie_len = data->length; + + alen = sizeof(*attach) + priv->wpa_ie_len; + attach = kzalloc(alen, GFP_KERNEL); + if (attach == NULL) + return -ENOMEM; + +#define WLAN_FC_TYPE_MGMT 0 +#define WLAN_FC_STYPE_ASSOC_REQ 0 +#define WLAN_FC_STYPE_REASSOC_REQ 2 + + /* Note: endianness is covered by mgt_set_varlen */ + attach->type = (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_ASSOC_REQ << 4); + attach->id = -1; + attach->size = priv->wpa_ie_len; + memcpy(attach->data, extra, priv->wpa_ie_len); + + ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach, + priv->wpa_ie_len); + if (ret == 0) { + attach->type = (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_REASSOC_REQ << 4); + + ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach, + priv->wpa_ie_len); + if (ret == 0) + printk(KERN_DEBUG "%s: WPA IE Attachment was set\n", + ndev->name); + } + + kfree(attach); + return ret; +} + + +static int prism54_get_genie(struct net_device *ndev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + int len = priv->wpa_ie_len; + + if (len <= 0) { + data->length = 0; + return 0; + } + + if (data->length < len) + return -E2BIG; + + data->length = len; + memcpy(extra, priv->wpa_ie, len); + + return 0; +} + +static int prism54_set_auth(struct net_device *ndev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct iw_param *param = &wrqu->param; + u32 mlmelevel = 0, authen = 0, dot1x = 0; + u32 exunencrypt = 0, privinvoked = 0, wpa = 0; + u32 old_wpa; + int ret = 0; + union oid_res_t r; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return 0; + + /* first get the flags */ + down_write(&priv->mib_sem); + wpa = old_wpa = priv->wpa; + up_write(&priv->mib_sem); + ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r); + authen = r.u; + ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r); + privinvoked = r.u; + ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r); + exunencrypt = r.u; + ret = mgt_get_request(priv, DOT11_OID_DOT1XENABLE, 0, NULL, &r); + dot1x = r.u; + ret = mgt_get_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, NULL, &r); + mlmelevel = r.u; + + if (ret < 0) + goto out; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + break; + + case IW_AUTH_WPA_ENABLED: + /* Do the same thing as IW_AUTH_WPA_VERSION */ + if (param->value) { + wpa = 1; + privinvoked = 1; /* For privacy invoked */ + exunencrypt = 1; /* Filter out all unencrypted frames */ + dot1x = 0x01; /* To enable eap filter */ + mlmelevel = DOT11_MLME_EXTENDED; + authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */ + } else { + wpa = 0; + privinvoked = 0; + exunencrypt = 0; /* Do not filter un-encrypted data */ + dot1x = 0; + mlmelevel = DOT11_MLME_AUTO; + } + break; + + case IW_AUTH_WPA_VERSION: + if (param->value & IW_AUTH_WPA_VERSION_DISABLED) { + wpa = 0; + privinvoked = 0; + exunencrypt = 0; /* Do not filter un-encrypted data */ + dot1x = 0; + mlmelevel = DOT11_MLME_AUTO; + } else { + if (param->value & IW_AUTH_WPA_VERSION_WPA) + wpa = 1; + else if (param->value & IW_AUTH_WPA_VERSION_WPA2) + wpa = 2; + privinvoked = 1; /* For privacy invoked */ + exunencrypt = 1; /* Filter out all unencrypted frames */ + dot1x = 0x01; /* To enable eap filter */ + mlmelevel = DOT11_MLME_EXTENDED; + authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */ + } + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + dot1x = param->value ? 1 : 0; + break; + + case IW_AUTH_PRIVACY_INVOKED: + privinvoked = param->value ? 1 : 0; + + case IW_AUTH_DROP_UNENCRYPTED: + exunencrypt = param->value ? 1 : 0; + break; + + case IW_AUTH_80211_AUTH_ALG: + if (param->value & IW_AUTH_ALG_SHARED_KEY) { + /* Only WEP uses _SK and _BOTH */ + if (wpa > 0) { + ret = -EINVAL; + goto out; + } + authen = DOT11_AUTH_SK; + } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { + authen = DOT11_AUTH_OS; + } else { + ret = -EINVAL; + goto out; + } + break; + + default: + return -EOPNOTSUPP; + } + + /* Set all the values */ + down_write(&priv->mib_sem); + priv->wpa = wpa; + up_write(&priv->mib_sem); + mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen); + mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &privinvoked); + mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, &exunencrypt); + mgt_set_request(priv, DOT11_OID_DOT1XENABLE, 0, &dot1x); + mgt_set_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, &mlmelevel); + +out: + return ret; +} + +static int prism54_get_auth(struct net_device *ndev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct iw_param *param = &wrqu->param; + u32 wpa = 0; + int ret = 0; + union oid_res_t r; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return 0; + + /* first get the flags */ + down_write(&priv->mib_sem); + wpa = priv->wpa; + up_write(&priv->mib_sem); + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * wpa_supplicant will control these internally + */ + ret = -EOPNOTSUPP; + break; + + case IW_AUTH_WPA_VERSION: + switch (wpa) { + case 1: + param->value = IW_AUTH_WPA_VERSION_WPA; + break; + case 2: + param->value = IW_AUTH_WPA_VERSION_WPA2; + break; + case 0: + default: + param->value = IW_AUTH_WPA_VERSION_DISABLED; + break; + } + break; + + case IW_AUTH_DROP_UNENCRYPTED: + ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r); + if (ret >= 0) + param->value = r.u > 0 ? 1 : 0; + break; + + case IW_AUTH_80211_AUTH_ALG: + ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r); + if (ret >= 0) { + switch (r.u) { + case DOT11_AUTH_OS: + param->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + case DOT11_AUTH_BOTH: + case DOT11_AUTH_SK: + param->value = IW_AUTH_ALG_SHARED_KEY; + case DOT11_AUTH_NONE: + default: + param->value = 0; + break; + } + } + break; + + case IW_AUTH_WPA_ENABLED: + param->value = wpa > 0 ? 1 : 0; + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + ret = mgt_get_request(priv, DOT11_OID_DOT1XENABLE, 0, NULL, &r); + if (ret >= 0) + param->value = r.u > 0 ? 1 : 0; + break; + + case IW_AUTH_PRIVACY_INVOKED: + ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r); + if (ret >= 0) + param->value = r.u > 0 ? 1 : 0; + break; + + default: + return -EOPNOTSUPP; + } + return ret; +} + +static int prism54_set_encodeext(struct net_device *ndev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, alg = ext->alg, set_key = 1; + union oid_res_t r; + int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0; + int ret = 0; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return 0; + + /* Determine and validate the key index */ + idx = (encoding->flags & IW_ENCODE_INDEX) - 1; + if (idx) { + if (idx < 0 || idx > 3) + return -EINVAL; + } else { + ret = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r); + if (ret < 0) + goto out; + idx = r.u; + } + + if (encoding->flags & IW_ENCODE_DISABLED) + alg = IW_ENCODE_ALG_NONE; + + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + /* Only set transmit key index here, actual + * key is set below if needed. + */ + ret = mgt_set_request(priv, DOT11_OID_DEFKEYID, 0, &idx); + set_key = ext->key_len > 0 ? 1 : 0; + } + + if (set_key) { + struct obj_key key = { DOT11_PRIV_WEP, 0, "" }; + switch (alg) { + case IW_ENCODE_ALG_NONE: + break; + case IW_ENCODE_ALG_WEP: + if (ext->key_len > KEY_SIZE_WEP104) { + ret = -EINVAL; + goto out; + } + if (ext->key_len > KEY_SIZE_WEP40) + key.length = KEY_SIZE_WEP104; + else + key.length = KEY_SIZE_WEP40; + break; + case IW_ENCODE_ALG_TKIP: + if (ext->key_len > KEY_SIZE_TKIP) { + ret = -EINVAL; + goto out; + } + key.type = DOT11_PRIV_TKIP; + key.length = KEY_SIZE_TKIP; + default: + return -EINVAL; + } + + if (key.length) { + memset(key.key, 0, sizeof(key.key)); + memcpy(key.key, ext->key, ext->key_len); + ret = mgt_set_request(priv, DOT11_OID_DEFKEYX, idx, + &key); + if (ret < 0) + goto out; + } + } + + /* Read the flags */ + if (encoding->flags & IW_ENCODE_DISABLED) { + /* Encoding disabled, + * authen = DOT11_AUTH_OS; + * invoke = 0; + * exunencrypt = 0; */ + } + if (encoding->flags & IW_ENCODE_OPEN) { + /* Encode but accept non-encoded packets. No auth */ + invoke = 1; + } + if (encoding->flags & IW_ENCODE_RESTRICTED) { + /* Refuse non-encoded packets. Auth */ + authen = DOT11_AUTH_BOTH; + invoke = 1; + exunencrypt = 1; + } + + /* do the change if requested */ + if (encoding->flags & IW_ENCODE_MODE) { + ret = mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, + &authen); + ret = mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, + &invoke); + ret = mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, + &exunencrypt); + } + +out: + return ret; +} + + +static int prism54_get_encodeext(struct net_device *ndev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, max_key_len; + union oid_res_t r; + int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0, wpa = 0; + int ret = 0; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return 0; + + /* first get the flags */ + ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r); + authen = r.u; + ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r); + invoke = r.u; + ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r); + exunencrypt = r.u; + if (ret < 0) + goto out; + + max_key_len = encoding->length - sizeof(*ext); + if (max_key_len < 0) + return -EINVAL; + + idx = (encoding->flags & IW_ENCODE_INDEX) - 1; + if (idx) { + if (idx < 0 || idx > 3) + return -EINVAL; + } else { + ret = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r); + if (ret < 0) + goto out; + idx = r.u; + } + + encoding->flags = idx + 1; + memset(ext, 0, sizeof(*ext)); + + switch (authen) { + case DOT11_AUTH_BOTH: + case DOT11_AUTH_SK: + wrqu->encoding.flags |= IW_ENCODE_RESTRICTED; + case DOT11_AUTH_OS: + default: + wrqu->encoding.flags |= IW_ENCODE_OPEN; + break; + } + + down_write(&priv->mib_sem); + wpa = priv->wpa; + up_write(&priv->mib_sem); + + if (authen == DOT11_AUTH_OS && !exunencrypt && !invoke && !wpa) { + /* No encryption */ + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + wrqu->encoding.flags |= IW_ENCODE_DISABLED; + } else { + struct obj_key *key; + + ret = mgt_get_request(priv, DOT11_OID_DEFKEYX, idx, NULL, &r); + if (ret < 0) + goto out; + key = r.ptr; + if (max_key_len < key->length) { + ret = -E2BIG; + goto out; + } + memcpy(ext->key, key->key, key->length); + ext->key_len = key->length; + + switch (key->type) { + case DOT11_PRIV_TKIP: + ext->alg = IW_ENCODE_ALG_TKIP; + break; + default: + case DOT11_PRIV_WEP: + ext->alg = IW_ENCODE_ALG_WEP; + break; + } + wrqu->encoding.flags |= IW_ENCODE_ENABLED; + } + +out: + return ret; +} + + static int prism54_reset(struct net_device *ndev, struct iw_request_info *info, __u32 * uwrq, char *extra) @@ -1591,8 +2081,8 @@ static u8 wpa_oid[4] = { 0x00, 0x50, 0xf2, 1 }; #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" static void -prism54_wpa_ie_add(islpci_private *priv, u8 *bssid, - u8 *wpa_ie, size_t wpa_ie_len) +prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid, + u8 *wpa_ie, size_t wpa_ie_len) { struct list_head *ptr; struct islpci_bss_wpa_ie *bss = NULL; @@ -1658,7 +2148,7 @@ prism54_wpa_ie_add(islpci_private *priv, u8 *bssid, } static size_t -prism54_wpa_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie) +prism54_wpa_bss_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie) { struct list_head *ptr; struct islpci_bss_wpa_ie *bss = NULL; @@ -1683,14 +2173,14 @@ prism54_wpa_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie) } void -prism54_wpa_ie_init(islpci_private *priv) +prism54_wpa_bss_ie_init(islpci_private *priv) { INIT_LIST_HEAD(&priv->bss_wpa_list); sema_init(&priv->wpa_sem, 1); } void -prism54_wpa_ie_clean(islpci_private *priv) +prism54_wpa_bss_ie_clean(islpci_private *priv) { struct list_head *ptr, *n; @@ -1722,7 +2212,7 @@ prism54_process_bss_data(islpci_private *priv, u32 oid, u8 *addr, } if (pos[0] == WLAN_EID_GENERIC && pos[1] >= 4 && memcmp(pos + 2, wpa_oid, 4) == 0) { - prism54_wpa_ie_add(priv, addr, pos, pos[1] + 2); + prism54_wpa_bss_ie_add(priv, addr, pos, pos[1] + 2); return; } pos += 2 + pos[1]; @@ -1879,7 +2369,7 @@ prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid, send_formatted_event(priv, "Associate request (ex)", mlme, 1); if (priv->iw_mode != IW_MODE_MASTER - && mlmeex->state != DOT11_STATE_AUTHING) + && mlmeex->state != DOT11_STATE_ASSOCING) break; confirm = kmalloc(sizeof(struct obj_mlmeex), GFP_ATOMIC); @@ -1893,7 +2383,7 @@ prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid, confirm->state = 0; /* not used */ confirm->code = 0; - wpa_ie_len = prism54_wpa_ie_get(priv, mlmeex->address, wpa_ie); + wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie); if (!wpa_ie_len) { printk(KERN_DEBUG "No WPA IE found from " @@ -1937,7 +2427,7 @@ prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid, confirm->state = 0; /* not used */ confirm->code = 0; - wpa_ie_len = prism54_wpa_ie_get(priv, mlmeex->address, wpa_ie); + wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie); if (!wpa_ie_len) { printk(KERN_DEBUG "No WPA IE found from " @@ -2553,6 +3043,15 @@ static const iw_handler prism54_handler[] = { (iw_handler) prism54_get_encode, /* SIOCGIWENCODE */ (iw_handler) NULL, /* SIOCSIWPOWER */ (iw_handler) NULL, /* SIOCGIWPOWER */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + (iw_handler) prism54_set_genie, /* SIOCSIWGENIE */ + (iw_handler) prism54_get_genie, /* SIOCGIWGENIE */ + (iw_handler) prism54_set_auth, /* SIOCSIWAUTH */ + (iw_handler) prism54_get_auth, /* SIOCGIWAUTH */ + (iw_handler) prism54_set_encodeext, /* SIOCSIWENCODEEXT */ + (iw_handler) prism54_get_encodeext, /* SIOCGIWENCODEEXT */ + NULL, /* SIOCSIWPMKSA */ }; /* The low order bit identify a SET (0) or a GET (1) ioctl. */ diff --git a/drivers/net/wireless/prism54/isl_ioctl.h b/drivers/net/wireless/prism54/isl_ioctl.h index 46d5cde80c8..65f33acd0a4 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.h +++ b/drivers/net/wireless/prism54/isl_ioctl.h @@ -27,7 +27,7 @@ #include <net/iw_handler.h> /* New driver API */ -#define SUPPORTED_WIRELESS_EXT 16 +#define SUPPORTED_WIRELESS_EXT 19 void prism54_mib_init(islpci_private *); @@ -39,8 +39,8 @@ void prism54_acl_clean(struct islpci_acl *); void prism54_process_trap(void *); -void prism54_wpa_ie_init(islpci_private *priv); -void prism54_wpa_ie_clean(islpci_private *priv); +void prism54_wpa_bss_ie_init(islpci_private *priv); +void prism54_wpa_bss_ie_clean(islpci_private *priv); int prism54_set_mac_address(struct net_device *, void *); diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c index 5ddf2959903..ab3c5a27efd 100644 --- a/drivers/net/wireless/prism54/islpci_dev.c +++ b/drivers/net/wireless/prism54/islpci_dev.c @@ -715,7 +715,7 @@ islpci_alloc_memory(islpci_private *priv) } prism54_acl_init(&priv->acl); - prism54_wpa_ie_init(priv); + prism54_wpa_bss_ie_init(priv); if (mgt_init(priv)) goto out_free; @@ -774,7 +774,7 @@ islpci_free_memory(islpci_private *priv) /* Free the acces control list and the WPA list */ prism54_acl_clean(&priv->acl); - prism54_wpa_ie_clean(priv); + prism54_wpa_bss_ie_clean(priv); mgt_clean(priv); return 0; diff --git a/drivers/net/wireless/prism54/islpci_dev.h b/drivers/net/wireless/prism54/islpci_dev.h index 07053165e4c..5049f37455b 100644 --- a/drivers/net/wireless/prism54/islpci_dev.h +++ b/drivers/net/wireless/prism54/islpci_dev.h @@ -179,6 +179,8 @@ typedef struct { struct list_head bss_wpa_list; int num_bss_wpa; struct semaphore wpa_sem; + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; struct work_struct reset_task; int reset_task_pending; diff --git a/drivers/net/wireless/zd1211rw/zd_netdev.c b/drivers/net/wireless/zd1211rw/zd_netdev.c index 9df232c2c86..440ef24b5fd 100644 --- a/drivers/net/wireless/zd1211rw/zd_netdev.c +++ b/drivers/net/wireless/zd1211rw/zd_netdev.c @@ -72,10 +72,18 @@ static int iw_get_name(struct net_device *netdev, struct iw_request_info *info, union iwreq_data *req, char *extra) { - /* FIXME: check whether 802.11a will also supported, add also - * zd1211B, if we support it. - */ - strlcpy(req->name, "802.11g zd1211", IFNAMSIZ); + /* FIXME: check whether 802.11a will also supported */ + strlcpy(req->name, "IEEE 802.11b/g", IFNAMSIZ); + return 0; +} + +static int iw_get_nick(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + strcpy(extra, "zd1211"); + req->data.length = strlen(extra) + 1; + req->data.flags = 1; return 0; } @@ -181,6 +189,7 @@ static int iw_get_encodeext(struct net_device *netdev, static const iw_handler zd_standard_iw_handlers[] = { WX(SIOCGIWNAME) = iw_get_name, + WX(SIOCGIWNICKN) = iw_get_nick, WX(SIOCSIWFREQ) = iw_set_freq, WX(SIOCGIWFREQ) = iw_get_freq, WX(SIOCSIWMODE) = iw_set_mode, diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 6320984126c..96551da769f 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -39,9 +39,11 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x079b, 0x004a), .driver_info = DEVICE_ZD1211 }, /* ZD1211B */ { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B }, {} }; diff --git a/include/net/ieee80211.h b/include/net/ieee80211.h index ecc42864b00..b174ebb277a 100644 --- a/include/net/ieee80211.h +++ b/include/net/ieee80211.h @@ -240,6 +240,11 @@ struct ieee80211_snap_hdr { #define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) #define WLAN_CAPABILITY_DSSS_OFDM (1<<13) +/* 802.11g ERP information element */ +#define WLAN_ERP_NON_ERP_PRESENT (1<<0) +#define WLAN_ERP_USE_PROTECTION (1<<1) +#define WLAN_ERP_BARKER_PREAMBLE (1<<2) + /* Status codes */ enum ieee80211_statuscode { WLAN_STATUS_SUCCESS = 0, @@ -747,6 +752,8 @@ struct ieee80211_txb { #define NETWORK_HAS_IBSS_DFS (1<<8) #define NETWORK_HAS_TPC_REPORT (1<<9) +#define NETWORK_HAS_ERP_VALUE (1<<10) + #define QOS_QUEUE_NUM 4 #define QOS_OUI_LEN 3 #define QOS_OUI_TYPE 2 @@ -1252,6 +1259,8 @@ extern int ieee80211_tx_frame(struct ieee80211_device *ieee, int total_len, int encrypt_mpdu); /* ieee80211_rx.c */ +extern void ieee80211_rx_any(struct ieee80211_device *ieee, + struct sk_buff *skb, struct ieee80211_rx_stats *stats); extern int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats); /* make sure to set stats->len */ diff --git a/include/net/ieee80211softmac.h b/include/net/ieee80211softmac.h index 00ad810eb88..425b3a57ac7 100644 --- a/include/net/ieee80211softmac.h +++ b/include/net/ieee80211softmac.h @@ -86,9 +86,6 @@ struct ieee80211softmac_assoc_info { /* BSSID we're trying to associate to */ char bssid[ETH_ALEN]; - - /* Rates supported by the network */ - struct ieee80211softmac_ratesinfo supported_rates; /* some flags. * static_essid is valid if the essid is constant, @@ -103,6 +100,7 @@ struct ieee80211softmac_assoc_info { * bssfixed is used for SIOCSIWAP. */ u8 static_essid:1, + short_preamble_available:1, associating:1, assoc_wait:1, bssvalid:1, @@ -115,6 +113,19 @@ struct ieee80211softmac_assoc_info { struct work_struct timeout; }; +struct ieee80211softmac_bss_info { + /* Rates supported by the network */ + struct ieee80211softmac_ratesinfo supported_rates; + + /* This indicates whether frames can currently be transmitted with + * short preamble (only use this variable during TX at CCK rates) */ + u8 short_preamble:1; + + /* This indicates whether protection (e.g. self-CTS) should be used + * when transmitting with OFDM modulation */ + u8 use_protection:1; +}; + enum { IEEE80211SOFTMAC_AUTH_OPEN_REQUEST = 1, IEEE80211SOFTMAC_AUTH_OPEN_RESPONSE = 2, @@ -157,6 +168,10 @@ struct ieee80211softmac_txrates { #define IEEE80211SOFTMAC_TXRATECHG_MCAST (1 << 2) /* mcast_rate */ #define IEEE80211SOFTMAC_TXRATECHG_MGT_MCAST (1 << 3) /* mgt_mcast_rate */ +#define IEEE80211SOFTMAC_BSSINFOCHG_RATES (1 << 0) /* supported_rates */ +#define IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE (1 << 1) /* short_preamble */ +#define IEEE80211SOFTMAC_BSSINFOCHG_PROTECTION (1 << 2) /* use_protection */ + struct ieee80211softmac_device { /* 802.11 structure for data stuff */ struct ieee80211_device *ieee; @@ -200,10 +215,16 @@ struct ieee80211softmac_device { * The driver just needs to read them. */ struct ieee80211softmac_txrates txrates; - /* If the driver needs to do stuff on TX rate changes, assign this callback. */ + + /* If the driver needs to do stuff on TX rate changes, assign this + * callback. See IEEE80211SOFTMAC_TXRATECHG for change flags. */ void (*txrates_change)(struct net_device *dev, - u32 changes, /* see IEEE80211SOFTMAC_TXRATECHG flags */ - const struct ieee80211softmac_txrates *rates_before_change); + u32 changes); + + /* If the driver needs to do stuff when BSS properties change, assign + * this callback. see IEEE80211SOFTMAC_BSSINFOCHG for change flags. */ + void (*bssinfo_change)(struct net_device *dev, + u32 changes); /* private stuff follows */ /* this lock protects this structure */ @@ -216,6 +237,7 @@ struct ieee80211softmac_device { struct ieee80211softmac_scaninfo *scaninfo; struct ieee80211softmac_assoc_info associnfo; + struct ieee80211softmac_bss_info bssinfo; struct list_head auth_queue; struct list_head events; @@ -257,6 +279,14 @@ extern void ieee80211softmac_fragment_lost(struct net_device *dev, * Note that the rates need to be sorted. */ extern void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates); +/* Finds the highest rate which is: + * 1. Present in ri (optionally a basic rate) + * 2. Supported by the device + * 3. Less than or equal to the user-defined rate + */ +extern u8 ieee80211softmac_highest_supported_rate(struct ieee80211softmac_device *mac, + struct ieee80211softmac_ratesinfo *ri, int basic_only); + /* Helper function which advises you the rate at which a frame should be * transmitted at. */ static inline u8 ieee80211softmac_suggest_txrate(struct ieee80211softmac_device *mac, @@ -279,6 +309,24 @@ static inline u8 ieee80211softmac_suggest_txrate(struct ieee80211softmac_device return txrates->mcast_rate; } +/* Helper function which advises you when it is safe to transmit with short + * preamble. + * You should only call this function when transmitting at CCK rates. */ +static inline int ieee80211softmac_short_preamble_ok(struct ieee80211softmac_device *mac, + int is_multicast, + int is_mgt) +{ + return (is_multicast && is_mgt) ? 0 : mac->bssinfo.short_preamble; +} + +/* Helper function which advises you whether protection (e.g. self-CTS) is + * needed. 1 = protection needed, 0 = no protection needed + * Only use this function when transmitting with OFDM modulation. */ +static inline int ieee80211softmac_protection_needed(struct ieee80211softmac_device *mac) +{ + return mac->bssinfo.use_protection; +} + /* Start the SoftMAC. Call this after you initialized the device * and it is ready to run. */ diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c index 72d4d4e04d4..d60358d702d 100644 --- a/net/ieee80211/ieee80211_rx.c +++ b/net/ieee80211/ieee80211_rx.c @@ -779,33 +779,44 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, return 0; } -/* Filter out unrelated packets, call ieee80211_rx[_mgt] */ -int ieee80211_rx_any(struct ieee80211_device *ieee, +/* Filter out unrelated packets, call ieee80211_rx[_mgt] + * This function takes over the skb, it should not be used again after calling + * this function. */ +void ieee80211_rx_any(struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *stats) { struct ieee80211_hdr_4addr *hdr; int is_packet_for_us; u16 fc; - if (ieee->iw_mode == IW_MODE_MONITOR) - return ieee80211_rx(ieee, skb, stats) ? 0 : -EINVAL; + if (ieee->iw_mode == IW_MODE_MONITOR) { + if (!ieee80211_rx(ieee, skb, stats)) + dev_kfree_skb_irq(skb); + return; + } + + if (skb->len < sizeof(struct ieee80211_hdr)) + goto drop_free; hdr = (struct ieee80211_hdr_4addr *)skb->data; fc = le16_to_cpu(hdr->frame_ctl); if ((fc & IEEE80211_FCTL_VERS) != 0) - return -EINVAL; + goto drop_free; switch (fc & IEEE80211_FCTL_FTYPE) { case IEEE80211_FTYPE_MGMT: + if (skb->len < sizeof(struct ieee80211_hdr_3addr)) + goto drop_free; ieee80211_rx_mgt(ieee, hdr, stats); - return 0; + dev_kfree_skb_irq(skb); + return; case IEEE80211_FTYPE_DATA: break; case IEEE80211_FTYPE_CTL: - return 0; + return; default: - return -EINVAL; + return; } is_packet_for_us = 0; @@ -849,8 +860,14 @@ int ieee80211_rx_any(struct ieee80211_device *ieee, } if (is_packet_for_us) - return (ieee80211_rx(ieee, skb, stats) ? 0 : -EINVAL); - return 0; + if (!ieee80211_rx(ieee, skb, stats)) + dev_kfree_skb_irq(skb); + return; + +drop_free: + dev_kfree_skb_irq(skb); + ieee->stats.rx_dropped++; + return; } #define MGMT_FRAME_FIXED_PART_LENGTH 0x24 @@ -1166,6 +1183,7 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element case MFIE_TYPE_ERP_INFO: network->erp_value = info_element->data[0]; + network->flags |= NETWORK_HAS_ERP_VALUE; IEEE80211_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n", network->erp_value); break; @@ -1729,5 +1747,6 @@ void ieee80211_rx_mgt(struct ieee80211_device *ieee, } } +EXPORT_SYMBOL_GPL(ieee80211_rx_any); EXPORT_SYMBOL(ieee80211_rx_mgt); EXPORT_SYMBOL(ieee80211_rx); diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c index 44215ce64d4..589f6d2c548 100644 --- a/net/ieee80211/softmac/ieee80211softmac_assoc.c +++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c @@ -96,7 +96,7 @@ ieee80211softmac_disassoc(struct ieee80211softmac_device *mac) mac->associated = 0; mac->associnfo.bssvalid = 0; mac->associnfo.associating = 0; - ieee80211softmac_init_txrates(mac); + ieee80211softmac_init_bss(mac); ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_DISASSOCIATED, NULL); spin_unlock_irqrestore(&mac->lock, flags); } @@ -334,11 +334,19 @@ ieee80211softmac_associated(struct ieee80211softmac_device *mac, struct ieee80211_assoc_response * resp, struct ieee80211softmac_network *net) { + u16 cap = le16_to_cpu(resp->capability); + u8 erp_value = net->erp_value; + mac->associnfo.associating = 0; - mac->associnfo.supported_rates = net->supported_rates; + mac->bssinfo.supported_rates = net->supported_rates; ieee80211softmac_recalc_txrates(mac); mac->associated = 1; + + mac->associnfo.short_preamble_available = + (cap & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0; + ieee80211softmac_process_erp(mac, erp_value); + if (mac->set_bssid_filter) mac->set_bssid_filter(mac->dev, net->bssid); memcpy(mac->ieee->bssid, net->bssid, ETH_ALEN); @@ -351,9 +359,9 @@ ieee80211softmac_associated(struct ieee80211softmac_device *mac, int ieee80211softmac_handle_assoc_response(struct net_device * dev, struct ieee80211_assoc_response * resp, - struct ieee80211_network * _ieee80211_network_do_not_use) + struct ieee80211_network * _ieee80211_network) { - /* NOTE: the network parameter has to be ignored by + /* NOTE: the network parameter has to be mostly ignored by * this code because it is the ieee80211's pointer * to the struct, not ours (we made a copy) */ @@ -385,6 +393,11 @@ ieee80211softmac_handle_assoc_response(struct net_device * dev, /* now that we know it was for us, we can cancel the timeout */ cancel_delayed_work(&mac->associnfo.timeout); + /* if the association response included an ERP IE, update our saved + * copy */ + if (_ieee80211_network->flags & NETWORK_HAS_ERP_VALUE) + network->erp_value = _ieee80211_network->erp_value; + switch (status) { case 0: dprintk(KERN_INFO PFX "associated!\n"); diff --git a/net/ieee80211/softmac/ieee80211softmac_io.c b/net/ieee80211/softmac/ieee80211softmac_io.c index 6ae5a1dc795..82bfddbf33a 100644 --- a/net/ieee80211/softmac/ieee80211softmac_io.c +++ b/net/ieee80211/softmac/ieee80211softmac_io.c @@ -467,3 +467,17 @@ ieee80211softmac_send_mgt_frame(struct ieee80211softmac_device *mac, kfree(pkt); return 0; } + +/* Beacon handling */ +int ieee80211softmac_handle_beacon(struct net_device *dev, + struct ieee80211_beacon *beacon, + struct ieee80211_network *network) +{ + struct ieee80211softmac_device *mac = ieee80211_priv(dev); + + if (mac->associated && memcmp(network->bssid, mac->associnfo.bssid, ETH_ALEN) == 0) + ieee80211softmac_process_erp(mac, network->erp_value); + + return 0; +} + diff --git a/net/ieee80211/softmac/ieee80211softmac_module.c b/net/ieee80211/softmac/ieee80211softmac_module.c index 4b2e57d1241..addea1cf73a 100644 --- a/net/ieee80211/softmac/ieee80211softmac_module.c +++ b/net/ieee80211/softmac/ieee80211softmac_module.c @@ -44,6 +44,7 @@ struct net_device *alloc_ieee80211softmac(int sizeof_priv) softmac->ieee->handle_assoc_response = ieee80211softmac_handle_assoc_response; softmac->ieee->handle_reassoc_request = ieee80211softmac_handle_reassoc_req; softmac->ieee->handle_disassoc = ieee80211softmac_handle_disassoc; + softmac->ieee->handle_beacon = ieee80211softmac_handle_beacon; softmac->scaninfo = NULL; softmac->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT; @@ -178,21 +179,14 @@ int ieee80211softmac_ratesinfo_rate_supported(struct ieee80211softmac_ratesinfo return 0; } -/* Finds the highest rate which is: - * 1. Present in ri (optionally a basic rate) - * 2. Supported by the device - * 3. Less than or equal to the user-defined rate - */ -static u8 highest_supported_rate(struct ieee80211softmac_device *mac, +u8 ieee80211softmac_highest_supported_rate(struct ieee80211softmac_device *mac, struct ieee80211softmac_ratesinfo *ri, int basic_only) { u8 user_rate = mac->txrates.user_rate; int i; - if (ri->count == 0) { - dprintk(KERN_ERR PFX "empty ratesinfo?\n"); + if (ri->count == 0) return IEEE80211_CCK_RATE_1MB; - } for (i = ri->count - 1; i >= 0; i--) { u8 rate = ri->rates[i]; @@ -208,36 +202,61 @@ static u8 highest_supported_rate(struct ieee80211softmac_device *mac, /* If we haven't found a suitable rate by now, just trust the user */ return user_rate; } +EXPORT_SYMBOL_GPL(ieee80211softmac_highest_supported_rate); + +void ieee80211softmac_process_erp(struct ieee80211softmac_device *mac, + u8 erp_value) +{ + int use_protection; + int short_preamble; + u32 changes = 0; + + /* Barker preamble mode */ + short_preamble = ((erp_value & WLAN_ERP_BARKER_PREAMBLE) == 0 + && mac->associnfo.short_preamble_available) ? 1 : 0; + + /* Protection needed? */ + use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0; + + if (mac->bssinfo.short_preamble != short_preamble) { + changes |= IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE; + mac->bssinfo.short_preamble = short_preamble; + } + + if (mac->bssinfo.use_protection != use_protection) { + changes |= IEEE80211SOFTMAC_BSSINFOCHG_PROTECTION; + mac->bssinfo.use_protection = use_protection; + } + + if (mac->bssinfo_change && changes) + mac->bssinfo_change(mac->dev, changes); +} void ieee80211softmac_recalc_txrates(struct ieee80211softmac_device *mac) { struct ieee80211softmac_txrates *txrates = &mac->txrates; - struct ieee80211softmac_txrates oldrates; u32 change = 0; - if (mac->txrates_change) - oldrates = mac->txrates; - change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT; - txrates->default_rate = highest_supported_rate(mac, &mac->associnfo.supported_rates, 0); + txrates->default_rate = ieee80211softmac_highest_supported_rate(mac, &mac->bssinfo.supported_rates, 0); change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK; txrates->default_fallback = lower_rate(mac, txrates->default_rate); change |= IEEE80211SOFTMAC_TXRATECHG_MCAST; - txrates->mcast_rate = highest_supported_rate(mac, &mac->associnfo.supported_rates, 1); + txrates->mcast_rate = ieee80211softmac_highest_supported_rate(mac, &mac->bssinfo.supported_rates, 1); if (mac->txrates_change) - mac->txrates_change(mac->dev, change, &oldrates); + mac->txrates_change(mac->dev, change); } -void ieee80211softmac_init_txrates(struct ieee80211softmac_device *mac) +void ieee80211softmac_init_bss(struct ieee80211softmac_device *mac) { struct ieee80211_device *ieee = mac->ieee; u32 change = 0; struct ieee80211softmac_txrates *txrates = &mac->txrates; - struct ieee80211softmac_txrates oldrates; + struct ieee80211softmac_bss_info *bssinfo = &mac->bssinfo; /* TODO: We need some kind of state machine to lower the default rates * if we loose too many packets. @@ -245,8 +264,6 @@ void ieee80211softmac_init_txrates(struct ieee80211softmac_device *mac) /* Change the default txrate to the highest possible value. * The txrate machine will lower it, if it is too high. */ - if (mac->txrates_change) - oldrates = mac->txrates; /* FIXME: We don't correctly handle backing down to lower rates, so 801.11g devices start off at 11M for now. People can manually change it if they really need to, but 11M is @@ -272,7 +289,23 @@ void ieee80211softmac_init_txrates(struct ieee80211softmac_device *mac) change |= IEEE80211SOFTMAC_TXRATECHG_MGT_MCAST; if (mac->txrates_change) - mac->txrates_change(mac->dev, change, &oldrates); + mac->txrates_change(mac->dev, change); + + change = 0; + + bssinfo->supported_rates.count = 0; + memset(bssinfo->supported_rates.rates, 0, + sizeof(bssinfo->supported_rates.rates)); + change |= IEEE80211SOFTMAC_BSSINFOCHG_RATES; + + bssinfo->short_preamble = 0; + change |= IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE; + + bssinfo->use_protection = 0; + change |= IEEE80211SOFTMAC_BSSINFOCHG_PROTECTION; + + if (mac->bssinfo_change) + mac->bssinfo_change(mac->dev, change); mac->running = 1; } @@ -282,7 +315,7 @@ void ieee80211softmac_start(struct net_device *dev) struct ieee80211softmac_device *mac = ieee80211_priv(dev); ieee80211softmac_start_check_rates(mac); - ieee80211softmac_init_txrates(mac); + ieee80211softmac_init_bss(mac); } EXPORT_SYMBOL_GPL(ieee80211softmac_start); @@ -335,7 +368,6 @@ u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rat static void ieee80211softmac_add_txrates_badness(struct ieee80211softmac_device *mac, int amount) { - struct ieee80211softmac_txrates oldrates; u8 default_rate = mac->txrates.default_rate; u8 default_fallback = mac->txrates.default_fallback; u32 changes = 0; @@ -348,8 +380,6 @@ printk("badness %d\n", mac->txrate_badness); mac->txrate_badness += amount; if (mac->txrate_badness <= -1000) { /* Very small badness. Try a faster bitrate. */ - if (mac->txrates_change) - memcpy(&oldrates, &mac->txrates, sizeof(oldrates)); default_rate = raise_rate(mac, default_rate); changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT; default_fallback = get_fallback_rate(mac, default_rate); @@ -358,8 +388,6 @@ printk("badness %d\n", mac->txrate_badness); printk("Bitrate raised to %u\n", default_rate); } else if (mac->txrate_badness >= 10000) { /* Very high badness. Try a slower bitrate. */ - if (mac->txrates_change) - memcpy(&oldrates, &mac->txrates, sizeof(oldrates)); default_rate = lower_rate(mac, default_rate); changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT; default_fallback = get_fallback_rate(mac, default_rate); @@ -372,7 +400,7 @@ printk("Bitrate lowered to %u\n", default_rate); mac->txrates.default_fallback = default_fallback; if (changes && mac->txrates_change) - mac->txrates_change(mac->dev, changes, &oldrates); + mac->txrates_change(mac->dev, changes); } void ieee80211softmac_fragment_lost(struct net_device *dev, @@ -416,7 +444,11 @@ ieee80211softmac_create_network(struct ieee80211softmac_device *mac, memcpy(&softnet->supported_rates.rates[softnet->supported_rates.count], net->rates_ex, net->rates_ex_len); softnet->supported_rates.count += net->rates_ex_len; sort(softnet->supported_rates.rates, softnet->supported_rates.count, sizeof(softnet->supported_rates.rates[0]), rate_cmp, NULL); - + + /* we save the ERP value because it is needed at association time, and + * many AP's do not include an ERP IE in the association response. */ + softnet->erp_value = net->erp_value; + softnet->capabilities = net->capability; return softnet; } diff --git a/net/ieee80211/softmac/ieee80211softmac_priv.h b/net/ieee80211/softmac/ieee80211softmac_priv.h index fa1f8e3acfc..0642e090b8a 100644 --- a/net/ieee80211/softmac/ieee80211softmac_priv.h +++ b/net/ieee80211/softmac/ieee80211softmac_priv.h @@ -116,9 +116,11 @@ ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac, struct ieee80211softmac_essid *essid); /* Rates related */ +void ieee80211softmac_process_erp(struct ieee80211softmac_device *mac, + u8 erp_value); int ieee80211softmac_ratesinfo_rate_supported(struct ieee80211softmac_ratesinfo *ri, u8 rate); u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta); -void ieee80211softmac_init_txrates(struct ieee80211softmac_device *mac); +void ieee80211softmac_init_bss(struct ieee80211softmac_device *mac); void ieee80211softmac_recalc_txrates(struct ieee80211softmac_device *mac); static inline u8 lower_rate(struct ieee80211softmac_device *mac, u8 rate) { return ieee80211softmac_lower_rate_delta(mac, rate, 1); @@ -133,6 +135,9 @@ static inline u8 get_fallback_rate(struct ieee80211softmac_device *mac, u8 rate) /*** prototypes from _io.c */ int ieee80211softmac_send_mgt_frame(struct ieee80211softmac_device *mac, void* ptrarg, u32 type, u32 arg); +int ieee80211softmac_handle_beacon(struct net_device *dev, + struct ieee80211_beacon *beacon, + struct ieee80211_network *network); /*** prototypes from _auth.c */ /* do these have to go into the public header? */ @@ -189,6 +194,7 @@ struct ieee80211softmac_network { authenticated:1, auth_desynced_once:1; + u8 erp_value; /* Saved ERP value */ u16 capabilities; /* Capabilities bitfield */ u8 challenge_len; /* Auth Challenge length */ char *challenge; /* Challenge Text */ |