···11+22+ BCM43xx Linux Driver Project33+ ============================44+55+About this software66+-------------------77+88+The goal of this project is to develop a linux driver for Broadcom99+BCM43xx chips, based on the specification at 1010+http://bcm-specs.sipsolutions.net/1111+1212+The project page is http://bcm43xx.berlios.de/1313+1414+1515+Requirements1616+------------1717+1818+1) Linux Kernel 2.6.16 or later1919+ http://www.kernel.org/2020+2121+ You may want to configure your kernel with:2222+2323+ CONFIG_DEBUG_FS (optional):2424+ -> Kernel hacking2525+ -> Debug Filesystem2626+2727+2) SoftMAC IEEE 802.11 Networking Stack extension and patched ieee802112828+ modules:2929+ http://softmac.sipsolutions.net/3030+3131+3) Firmware Files3232+3333+ Please try fwcutter. Fwcutter can extract the firmware from various 3434+ binary driver files. It supports driver files from Windows, MacOS and 3535+ Linux. You can get fwcutter from http://bcm43xx.berlios.de/.3636+ Also, fwcutter comes with a README file for further instructions.
+6-1
drivers/net/wireless/Kconfig
···309309 Say Y here to support the Airport 802.11b wireless Ethernet hardware310310 built into the Macintosh iBook and other recent PowerPC-based311311 Macintosh machines. This is essentially a Lucent Orinoco card with 312312- a non-standard interface312312+ a non-standard interface.313313+314314+ This driver does not support the Airport Extreme (802.11b/g). Use315315+ the BCM43xx driver for Airport Extreme cards.313316314317config PLX_HERMES315318 tristate "Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.)"···404401config PCMCIA_SPECTRUM405402 tristate "Symbol Spectrum24 Trilogy PCMCIA card support"406403 depends on NET_RADIO && PCMCIA && HERMES404404+ select FW_LOADER407405 ---help---408406409407 This is a driver for 802.11b cards using RAM-loadable Symbol···504500 will be called prism54.ko.505501506502source "drivers/net/wireless/hostap/Kconfig"503503+source "drivers/net/wireless/bcm43xx/Kconfig"507504508505# yes, this works even when no drivers are selected509506config NET_WIRELESS
···11+config BCM43XX22+ tristate "Broadcom BCM43xx wireless support"33+ depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL44+ select FW_LOADER55+ ---help---66+ This is an experimental driver for the Broadcom 43xx wireless chip,77+ found in the Apple Airport Extreme and various other devices.88+99+config BCM43XX_DEBUG1010+ bool "Broadcom BCM43xx debugging (RECOMMENDED)"1111+ depends on BCM43XX1212+ default y1313+ ---help---1414+ Broadcom 43xx debugging messages.1515+ Say Y, because the driver is still very experimental and1616+ this will help you get it running.1717+1818+config BCM43XX_DMA1919+ bool2020+config BCM43XX_PIO2121+ bool2222+2323+choice2424+ prompt "BCM43xx data transfer mode"2525+ depends on BCM43XX2626+ default BCM43XX_DMA_AND_PIO_MODE2727+2828+config BCM43XX_DMA_AND_PIO_MODE2929+ bool "DMA + PIO"3030+ select BCM43XX_DMA3131+ select BCM43XX_PIO3232+ ---help---3333+ Include both, Direct Memory Access (DMA) and Programmed I/O (PIO)3434+ data transfer modes.3535+ The actually used mode is selectable through the module3636+ parameter "pio". If the module parameter is pio=0, DMA is used.3737+ Otherwise PIO is used. DMA is default.3838+3939+ If unsure, choose this option.4040+4141+config BCM43XX_DMA_MODE4242+ bool "DMA (Direct Memory Access) only"4343+ select BCM43XX_DMA4444+ ---help---4545+ Only include Direct Memory Access (DMA).4646+ This reduces the size of the driver module, by omitting the PIO code.4747+4848+config BCM43XX_PIO_MODE4949+ bool "PIO (Programmed I/O) only"5050+ select BCM43XX_PIO5151+ ---help---5252+ Only include Programmed I/O (PIO).5353+ This reduces the size of the driver module, by omitting the DMA code.5454+ Please note that PIO transfers are slow (compared to DMA).5555+5656+ Also note that not all devices of the 43xx series support PIO.5757+ The 4306 (Apple Airport Extreme and others) supports PIO, while5858+ the 4318 is known to _not_ support PIO.5959+6060+ Only use PIO, if DMA does not work for you.6161+6262+endchoice
···11+#ifndef BCM43xx_H_22+#define BCM43xx_H_33+44+#include <linux/version.h>55+#include <linux/kernel.h>66+#include <linux/spinlock.h>77+#include <linux/interrupt.h>88+#include <linux/stringify.h>99+#include <linux/pci.h>1010+#include <net/ieee80211.h>1111+#include <net/ieee80211softmac.h>1212+#include <asm/atomic.h>1313+#include <asm/io.h>1414+1515+1616+#include "bcm43xx_debugfs.h"1717+#include "bcm43xx_leds.h"1818+#include "bcm43xx_sysfs.h"1919+2020+2121+#define PFX KBUILD_MODNAME ": "2222+2323+#define BCM43xx_SWITCH_CORE_MAX_RETRIES 502424+#define BCM43xx_IRQWAIT_MAX_RETRIES 502525+2626+#define BCM43xx_IO_SIZE 81922727+2828+/* Active Core PCI Configuration Register. */2929+#define BCM43xx_PCICFG_ACTIVE_CORE 0x803030+/* SPROM control register. */3131+#define BCM43xx_PCICFG_SPROMCTL 0x883232+/* Interrupt Control PCI Configuration Register. (Only on PCI cores with rev >= 6) */3333+#define BCM43xx_PCICFG_ICR 0x943434+3535+/* MMIO offsets */3636+#define BCM43xx_MMIO_DMA1_REASON 0x203737+#define BCM43xx_MMIO_DMA1_IRQ_MASK 0x243838+#define BCM43xx_MMIO_DMA2_REASON 0x283939+#define BCM43xx_MMIO_DMA2_IRQ_MASK 0x2C4040+#define BCM43xx_MMIO_DMA3_REASON 0x304141+#define BCM43xx_MMIO_DMA3_IRQ_MASK 0x344242+#define BCM43xx_MMIO_DMA4_REASON 0x384343+#define BCM43xx_MMIO_DMA4_IRQ_MASK 0x3C4444+#define BCM43xx_MMIO_STATUS_BITFIELD 0x1204545+#define BCM43xx_MMIO_STATUS2_BITFIELD 0x1244646+#define BCM43xx_MMIO_GEN_IRQ_REASON 0x1284747+#define BCM43xx_MMIO_GEN_IRQ_MASK 0x12C4848+#define BCM43xx_MMIO_RAM_CONTROL 0x1304949+#define BCM43xx_MMIO_RAM_DATA 0x1345050+#define BCM43xx_MMIO_PS_STATUS 0x1405151+#define BCM43xx_MMIO_RADIO_HWENABLED_HI 0x1585252+#define BCM43xx_MMIO_SHM_CONTROL 0x1605353+#define BCM43xx_MMIO_SHM_DATA 0x1645454+#define BCM43xx_MMIO_SHM_DATA_UNALIGNED 0x1665555+#define BCM43xx_MMIO_XMITSTAT_0 0x1705656+#define BCM43xx_MMIO_XMITSTAT_1 0x1745757+#define BCM43xx_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */5858+#define BCM43xx_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */5959+#define BCM43xx_MMIO_DMA1_BASE 0x2006060+#define BCM43xx_MMIO_DMA2_BASE 0x2206161+#define BCM43xx_MMIO_DMA3_BASE 0x2406262+#define BCM43xx_MMIO_DMA4_BASE 0x2606363+#define BCM43xx_MMIO_PIO1_BASE 0x3006464+#define BCM43xx_MMIO_PIO2_BASE 0x3106565+#define BCM43xx_MMIO_PIO3_BASE 0x3206666+#define BCM43xx_MMIO_PIO4_BASE 0x3306767+#define BCM43xx_MMIO_PHY_VER 0x3E06868+#define BCM43xx_MMIO_PHY_RADIO 0x3E26969+#define BCM43xx_MMIO_ANTENNA 0x3E87070+#define BCM43xx_MMIO_CHANNEL 0x3F07171+#define BCM43xx_MMIO_CHANNEL_EXT 0x3F47272+#define BCM43xx_MMIO_RADIO_CONTROL 0x3F67373+#define BCM43xx_MMIO_RADIO_DATA_HIGH 0x3F87474+#define BCM43xx_MMIO_RADIO_DATA_LOW 0x3FA7575+#define BCM43xx_MMIO_PHY_CONTROL 0x3FC7676+#define BCM43xx_MMIO_PHY_DATA 0x3FE7777+#define BCM43xx_MMIO_MACFILTER_CONTROL 0x4207878+#define BCM43xx_MMIO_MACFILTER_DATA 0x4227979+#define BCM43xx_MMIO_RADIO_HWENABLED_LO 0x49A8080+#define BCM43xx_MMIO_GPIO_CONTROL 0x49C8181+#define BCM43xx_MMIO_GPIO_MASK 0x49E8282+#define BCM43xx_MMIO_TSF_0 0x632 /* core rev < 3 only */8383+#define BCM43xx_MMIO_TSF_1 0x634 /* core rev < 3 only */8484+#define BCM43xx_MMIO_TSF_2 0x636 /* core rev < 3 only */8585+#define BCM43xx_MMIO_TSF_3 0x638 /* core rev < 3 only */8686+#define BCM43xx_MMIO_POWERUP_DELAY 0x6A88787+8888+/* SPROM offsets. */8989+#define BCM43xx_SPROM_BASE 0x10009090+#define BCM43xx_SPROM_BOARDFLAGS2 0x1c9191+#define BCM43xx_SPROM_IL0MACADDR 0x249292+#define BCM43xx_SPROM_ET0MACADDR 0x279393+#define BCM43xx_SPROM_ET1MACADDR 0x2a9494+#define BCM43xx_SPROM_ETHPHY 0x2d9595+#define BCM43xx_SPROM_BOARDREV 0x2e9696+#define BCM43xx_SPROM_PA0B0 0x2f9797+#define BCM43xx_SPROM_PA0B1 0x309898+#define BCM43xx_SPROM_PA0B2 0x319999+#define BCM43xx_SPROM_WL0GPIO0 0x32100100+#define BCM43xx_SPROM_WL0GPIO2 0x33101101+#define BCM43xx_SPROM_MAXPWR 0x34102102+#define BCM43xx_SPROM_PA1B0 0x35103103+#define BCM43xx_SPROM_PA1B1 0x36104104+#define BCM43xx_SPROM_PA1B2 0x37105105+#define BCM43xx_SPROM_IDL_TSSI_TGT 0x38106106+#define BCM43xx_SPROM_BOARDFLAGS 0x39107107+#define BCM43xx_SPROM_ANTENNA_GAIN 0x3a108108+#define BCM43xx_SPROM_VERSION 0x3f109109+110110+/* BCM43xx_SPROM_BOARDFLAGS values */111111+#define BCM43xx_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */112112+#define BCM43xx_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */113113+#define BCM43xx_BFL_AIRLINEMODE 0x0004 /* implements GPIO 13 radio disable indication */114114+#define BCM43xx_BFL_RSSI 0x0008 /* software calculates nrssi slope. */115115+#define BCM43xx_BFL_ENETSPI 0x0010 /* has ephy roboswitch spi */116116+#define BCM43xx_BFL_XTAL_NOSLOW 0x0020 /* no slow clock available */117117+#define BCM43xx_BFL_CCKHIPWR 0x0040 /* can do high power CCK transmission */118118+#define BCM43xx_BFL_ENETADM 0x0080 /* has ADMtek switch */119119+#define BCM43xx_BFL_ENETVLAN 0x0100 /* can do vlan */120120+#define BCM43xx_BFL_AFTERBURNER 0x0200 /* supports Afterburner mode */121121+#define BCM43xx_BFL_NOPCI 0x0400 /* leaves PCI floating */122122+#define BCM43xx_BFL_FEM 0x0800 /* supports the Front End Module */123123+#define BCM43xx_BFL_EXTLNA 0x1000 /* has an external LNA */124124+#define BCM43xx_BFL_HGPA 0x2000 /* had high gain PA */125125+#define BCM43xx_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */126126+#define BCM43xx_BFL_ALTIQ 0x8000 /* alternate I/Q settings */127127+128128+/* GPIO register offset, in both ChipCommon and PCI core. */129129+#define BCM43xx_GPIO_CONTROL 0x6c130130+131131+/* SHM Routing */132132+#define BCM43xx_SHM_SHARED 0x0001133133+#define BCM43xx_SHM_WIRELESS 0x0002134134+#define BCM43xx_SHM_PCM 0x0003135135+#define BCM43xx_SHM_HWMAC 0x0004136136+#define BCM43xx_SHM_UCODE 0x0300137137+138138+/* MacFilter offsets. */139139+#define BCM43xx_MACFILTER_SELF 0x0000140140+#define BCM43xx_MACFILTER_ASSOC 0x0003141141+142142+/* Chipcommon registers. */143143+#define BCM43xx_CHIPCOMMON_CAPABILITIES 0x04144144+#define BCM43xx_CHIPCOMMON_PLLONDELAY 0xB0145145+#define BCM43xx_CHIPCOMMON_FREFSELDELAY 0xB4146146+#define BCM43xx_CHIPCOMMON_SLOWCLKCTL 0xB8147147+#define BCM43xx_CHIPCOMMON_SYSCLKCTL 0xC0148148+149149+/* PCI core specific registers. */150150+#define BCM43xx_PCICORE_BCAST_ADDR 0x50151151+#define BCM43xx_PCICORE_BCAST_DATA 0x54152152+#define BCM43xx_PCICORE_SBTOPCI2 0x108153153+154154+/* SBTOPCI2 values. */155155+#define BCM43xx_SBTOPCI2_PREFETCH 0x4156156+#define BCM43xx_SBTOPCI2_BURST 0x8157157+158158+/* Chipcommon capabilities. */159159+#define BCM43xx_CAPABILITIES_PCTL 0x00040000160160+#define BCM43xx_CAPABILITIES_PLLMASK 0x00030000161161+#define BCM43xx_CAPABILITIES_PLLSHIFT 16162162+#define BCM43xx_CAPABILITIES_FLASHMASK 0x00000700163163+#define BCM43xx_CAPABILITIES_FLASHSHIFT 8164164+#define BCM43xx_CAPABILITIES_EXTBUSPRESENT 0x00000040165165+#define BCM43xx_CAPABILITIES_UARTGPIO 0x00000020166166+#define BCM43xx_CAPABILITIES_UARTCLOCKMASK 0x00000018167167+#define BCM43xx_CAPABILITIES_UARTCLOCKSHIFT 3168168+#define BCM43xx_CAPABILITIES_MIPSBIGENDIAN 0x00000004169169+#define BCM43xx_CAPABILITIES_NRUARTSMASK 0x00000003170170+171171+/* PowerControl */172172+#define BCM43xx_PCTL_IN 0xB0173173+#define BCM43xx_PCTL_OUT 0xB4174174+#define BCM43xx_PCTL_OUTENABLE 0xB8175175+#define BCM43xx_PCTL_XTAL_POWERUP 0x40176176+#define BCM43xx_PCTL_PLL_POWERDOWN 0x80177177+178178+/* PowerControl Clock Modes */179179+#define BCM43xx_PCTL_CLK_FAST 0x00180180+#define BCM43xx_PCTL_CLK_SLOW 0x01181181+#define BCM43xx_PCTL_CLK_DYNAMIC 0x02182182+183183+#define BCM43xx_PCTL_FORCE_SLOW 0x0800184184+#define BCM43xx_PCTL_FORCE_PLL 0x1000185185+#define BCM43xx_PCTL_DYN_XTAL 0x2000186186+187187+/* COREIDs */188188+#define BCM43xx_COREID_CHIPCOMMON 0x800189189+#define BCM43xx_COREID_ILINE20 0x801190190+#define BCM43xx_COREID_SDRAM 0x803191191+#define BCM43xx_COREID_PCI 0x804192192+#define BCM43xx_COREID_MIPS 0x805193193+#define BCM43xx_COREID_ETHERNET 0x806194194+#define BCM43xx_COREID_V90 0x807195195+#define BCM43xx_COREID_USB11_HOSTDEV 0x80a196196+#define BCM43xx_COREID_IPSEC 0x80b197197+#define BCM43xx_COREID_PCMCIA 0x80d198198+#define BCM43xx_COREID_EXT_IF 0x80f199199+#define BCM43xx_COREID_80211 0x812200200+#define BCM43xx_COREID_MIPS_3302 0x816201201+#define BCM43xx_COREID_USB11_HOST 0x817202202+#define BCM43xx_COREID_USB11_DEV 0x818203203+#define BCM43xx_COREID_USB20_HOST 0x819204204+#define BCM43xx_COREID_USB20_DEV 0x81a205205+#define BCM43xx_COREID_SDIO_HOST 0x81b206206+207207+/* Core Information Registers */208208+#define BCM43xx_CIR_BASE 0xf00209209+#define BCM43xx_CIR_SBTPSFLAG (BCM43xx_CIR_BASE + 0x18)210210+#define BCM43xx_CIR_SBIMSTATE (BCM43xx_CIR_BASE + 0x90)211211+#define BCM43xx_CIR_SBINTVEC (BCM43xx_CIR_BASE + 0x94)212212+#define BCM43xx_CIR_SBTMSTATELOW (BCM43xx_CIR_BASE + 0x98)213213+#define BCM43xx_CIR_SBTMSTATEHIGH (BCM43xx_CIR_BASE + 0x9c)214214+#define BCM43xx_CIR_SBIMCONFIGLOW (BCM43xx_CIR_BASE + 0xa8)215215+#define BCM43xx_CIR_SB_ID_HI (BCM43xx_CIR_BASE + 0xfc)216216+217217+/* Mask to get the Backplane Flag Number from SBTPSFLAG. */218218+#define BCM43xx_BACKPLANE_FLAG_NR_MASK 0x3f219219+220220+/* SBIMCONFIGLOW values/masks. */221221+#define BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK 0x00000007222222+#define BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_SHIFT 0223223+#define BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK 0x00000070224224+#define BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_SHIFT 4225225+#define BCM43xx_SBIMCONFIGLOW_CONNID_MASK 0x00ff0000226226+#define BCM43xx_SBIMCONFIGLOW_CONNID_SHIFT 16227227+228228+/* sbtmstatelow state flags */229229+#define BCM43xx_SBTMSTATELOW_RESET 0x01230230+#define BCM43xx_SBTMSTATELOW_REJECT 0x02231231+#define BCM43xx_SBTMSTATELOW_CLOCK 0x10000232232+#define BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK 0x20000233233+234234+/* sbtmstatehigh state flags */235235+#define BCM43xx_SBTMSTATEHIGH_SERROR 0x1236236+#define BCM43xx_SBTMSTATEHIGH_BUSY 0x4237237+238238+/* sbimstate flags */239239+#define BCM43xx_SBIMSTATE_IB_ERROR 0x20000240240+#define BCM43xx_SBIMSTATE_TIMEOUT 0x40000241241+242242+/* PHYVersioning */243243+#define BCM43xx_PHYTYPE_A 0x00244244+#define BCM43xx_PHYTYPE_B 0x01245245+#define BCM43xx_PHYTYPE_G 0x02246246+247247+/* PHYRegisters */248248+#define BCM43xx_PHY_ILT_A_CTRL 0x0072249249+#define BCM43xx_PHY_ILT_A_DATA1 0x0073250250+#define BCM43xx_PHY_ILT_A_DATA2 0x0074251251+#define BCM43xx_PHY_G_LO_CONTROL 0x0810252252+#define BCM43xx_PHY_ILT_G_CTRL 0x0472253253+#define BCM43xx_PHY_ILT_G_DATA1 0x0473254254+#define BCM43xx_PHY_ILT_G_DATA2 0x0474255255+#define BCM43xx_PHY_A_PCTL 0x007B256256+#define BCM43xx_PHY_G_PCTL 0x0029257257+#define BCM43xx_PHY_A_CRS 0x0029258258+#define BCM43xx_PHY_RADIO_BITFIELD 0x0401259259+#define BCM43xx_PHY_G_CRS 0x0429260260+#define BCM43xx_PHY_NRSSILT_CTRL 0x0803261261+#define BCM43xx_PHY_NRSSILT_DATA 0x0804262262+263263+/* RadioRegisters */264264+#define BCM43xx_RADIOCTL_ID 0x01265265+266266+/* StatusBitField */267267+#define BCM43xx_SBF_MAC_ENABLED 0x00000001268268+#define BCM43xx_SBF_2 0x00000002 /*FIXME: fix name*/269269+#define BCM43xx_SBF_CORE_READY 0x00000004270270+#define BCM43xx_SBF_400 0x00000400 /*FIXME: fix name*/271271+#define BCM43xx_SBF_4000 0x00004000 /*FIXME: fix name*/272272+#define BCM43xx_SBF_8000 0x00008000 /*FIXME: fix name*/273273+#define BCM43xx_SBF_XFER_REG_BYTESWAP 0x00010000274274+#define BCM43xx_SBF_MODE_NOTADHOC 0x00020000275275+#define BCM43xx_SBF_MODE_AP 0x00040000276276+#define BCM43xx_SBF_RADIOREG_LOCK 0x00080000277277+#define BCM43xx_SBF_MODE_MONITOR 0x00400000278278+#define BCM43xx_SBF_MODE_PROMISC 0x01000000279279+#define BCM43xx_SBF_PS1 0x02000000280280+#define BCM43xx_SBF_PS2 0x04000000281281+#define BCM43xx_SBF_NO_SSID_BCAST 0x08000000282282+#define BCM43xx_SBF_TIME_UPDATE 0x10000000283283+#define BCM43xx_SBF_80000000 0x80000000 /*FIXME: fix name*/284284+285285+/* MicrocodeFlagsBitfield (addr + lo-word values?)*/286286+#define BCM43xx_UCODEFLAGS_OFFSET 0x005E287287+288288+#define BCM43xx_UCODEFLAG_AUTODIV 0x0001289289+#define BCM43xx_UCODEFLAG_UNKBGPHY 0x0002290290+#define BCM43xx_UCODEFLAG_UNKBPHY 0x0004291291+#define BCM43xx_UCODEFLAG_UNKGPHY 0x0020292292+#define BCM43xx_UCODEFLAG_UNKPACTRL 0x0040293293+#define BCM43xx_UCODEFLAG_JAPAN 0x0080294294+295295+/* Generic-Interrupt reasons. */296296+#define BCM43xx_IRQ_READY (1 << 0)297297+#define BCM43xx_IRQ_BEACON (1 << 1)298298+#define BCM43xx_IRQ_PS (1 << 2)299299+#define BCM43xx_IRQ_REG124 (1 << 5)300300+#define BCM43xx_IRQ_PMQ (1 << 6)301301+#define BCM43xx_IRQ_PIO_WORKAROUND (1 << 8)302302+#define BCM43xx_IRQ_XMIT_ERROR (1 << 11)303303+#define BCM43xx_IRQ_RX (1 << 15)304304+#define BCM43xx_IRQ_SCAN (1 << 16)305305+#define BCM43xx_IRQ_NOISE (1 << 18)306306+#define BCM43xx_IRQ_XMIT_STATUS (1 << 29)307307+308308+#define BCM43xx_IRQ_ALL 0xffffffff309309+#define BCM43xx_IRQ_INITIAL (BCM43xx_IRQ_PS | \310310+ BCM43xx_IRQ_REG124 | \311311+ BCM43xx_IRQ_PMQ | \312312+ BCM43xx_IRQ_XMIT_ERROR | \313313+ BCM43xx_IRQ_RX | \314314+ BCM43xx_IRQ_SCAN | \315315+ BCM43xx_IRQ_NOISE | \316316+ BCM43xx_IRQ_XMIT_STATUS)317317+318318+319319+/* Initial default iw_mode */320320+#define BCM43xx_INITIAL_IWMODE IW_MODE_INFRA321321+322322+/* Bus type PCI. */323323+#define BCM43xx_BUSTYPE_PCI 0324324+/* Bus type Silicone Backplane Bus. */325325+#define BCM43xx_BUSTYPE_SB 1326326+/* Bus type PCMCIA. */327327+#define BCM43xx_BUSTYPE_PCMCIA 2328328+329329+/* Threshold values. */330330+#define BCM43xx_MIN_RTS_THRESHOLD 1U331331+#define BCM43xx_MAX_RTS_THRESHOLD 2304U332332+#define BCM43xx_DEFAULT_RTS_THRESHOLD BCM43xx_MAX_RTS_THRESHOLD333333+334334+#define BCM43xx_DEFAULT_SHORT_RETRY_LIMIT 7335335+#define BCM43xx_DEFAULT_LONG_RETRY_LIMIT 4336336+337337+/* Max size of a security key */338338+#define BCM43xx_SEC_KEYSIZE 16339339+/* Security algorithms. */340340+enum {341341+ BCM43xx_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */342342+ BCM43xx_SEC_ALGO_WEP,343343+ BCM43xx_SEC_ALGO_UNKNOWN,344344+ BCM43xx_SEC_ALGO_AES,345345+ BCM43xx_SEC_ALGO_WEP104,346346+ BCM43xx_SEC_ALGO_TKIP,347347+};348348+349349+#ifdef assert350350+# undef assert351351+#endif352352+#ifdef CONFIG_BCM43XX_DEBUG353353+#define assert(expr) \354354+ do { \355355+ if (unlikely(!(expr))) { \356356+ printk(KERN_ERR PFX "ASSERTION FAILED (%s) at: %s:%d:%s()\n", \357357+ #expr, __FILE__, __LINE__, __FUNCTION__); \358358+ } \359359+ } while (0)360360+#else361361+#define assert(expr) do { /* nothing */ } while (0)362362+#endif363363+364364+/* rate limited printk(). */365365+#ifdef printkl366366+# undef printkl367367+#endif368368+#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0)369369+/* rate limited printk() for debugging */370370+#ifdef dprintkl371371+# undef dprintkl372372+#endif373373+#ifdef CONFIG_BCM43XX_DEBUG374374+# define dprintkl printkl375375+#else376376+# define dprintkl(f, x...) do { /* nothing */ } while (0)377377+#endif378378+379379+/* Helper macro for if branches.380380+ * An if branch marked with this macro is only taken in DEBUG mode.381381+ * Example:382382+ * if (DEBUG_ONLY(foo == bar)) {383383+ * do something384384+ * }385385+ * In DEBUG mode, the branch will be taken if (foo == bar).386386+ * In non-DEBUG mode, the branch will never be taken.387387+ */388388+#ifdef DEBUG_ONLY389389+# undef DEBUG_ONLY390390+#endif391391+#ifdef CONFIG_BCM43XX_DEBUG392392+# define DEBUG_ONLY(x) (x)393393+#else394394+# define DEBUG_ONLY(x) 0395395+#endif396396+397397+/* debugging printk() */398398+#ifdef dprintk399399+# undef dprintk400400+#endif401401+#ifdef CONFIG_BCM43XX_DEBUG402402+# define dprintk(f, x...) do { printk(f ,##x); } while (0)403403+#else404404+# define dprintk(f, x...) do { /* nothing */ } while (0)405405+#endif406406+407407+408408+struct net_device;409409+struct pci_dev;410410+struct bcm43xx_dmaring;411411+struct bcm43xx_pioqueue;412412+413413+struct bcm43xx_initval {414414+ u16 offset;415415+ u16 size;416416+ u32 value;417417+} __attribute__((__packed__));418418+419419+/* Values for bcm430x_sprominfo.locale */420420+enum {421421+ BCM43xx_LOCALE_WORLD = 0,422422+ BCM43xx_LOCALE_THAILAND,423423+ BCM43xx_LOCALE_ISRAEL,424424+ BCM43xx_LOCALE_JORDAN,425425+ BCM43xx_LOCALE_CHINA,426426+ BCM43xx_LOCALE_JAPAN,427427+ BCM43xx_LOCALE_USA_CANADA_ANZ,428428+ BCM43xx_LOCALE_EUROPE,429429+ BCM43xx_LOCALE_USA_LOW,430430+ BCM43xx_LOCALE_JAPAN_HIGH,431431+ BCM43xx_LOCALE_ALL,432432+ BCM43xx_LOCALE_NONE,433433+};434434+435435+#define BCM43xx_SPROM_SIZE 64 /* in 16-bit words. */436436+struct bcm43xx_sprominfo {437437+ u16 boardflags2;438438+ u8 il0macaddr[6];439439+ u8 et0macaddr[6];440440+ u8 et1macaddr[6];441441+ u8 et0phyaddr:5;442442+ u8 et1phyaddr:5;443443+ u8 et0mdcport:1;444444+ u8 et1mdcport:1;445445+ u8 boardrev;446446+ u8 locale:4;447447+ u8 antennas_aphy:2;448448+ u8 antennas_bgphy:2;449449+ u16 pa0b0;450450+ u16 pa0b1;451451+ u16 pa0b2;452452+ u8 wl0gpio0;453453+ u8 wl0gpio1;454454+ u8 wl0gpio2;455455+ u8 wl0gpio3;456456+ u8 maxpower_aphy;457457+ u8 maxpower_bgphy;458458+ u16 pa1b0;459459+ u16 pa1b1;460460+ u16 pa1b2;461461+ u8 idle_tssi_tgt_aphy;462462+ u8 idle_tssi_tgt_bgphy;463463+ u16 boardflags;464464+ u16 antennagain_aphy;465465+ u16 antennagain_bgphy;466466+};467467+468468+/* Value pair to measure the LocalOscillator. */469469+struct bcm43xx_lopair {470470+ s8 low;471471+ s8 high;472472+ u8 used:1;473473+};474474+#define BCM43xx_LO_COUNT (14*4)475475+476476+struct bcm43xx_phyinfo {477477+ /* Hardware Data */478478+ u8 version;479479+ u8 type;480480+ u8 rev;481481+ u16 antenna_diversity;482482+ u16 savedpctlreg;483483+ u16 minlowsig[2];484484+ u16 minlowsigpos[2];485485+ u8 connected:1,486486+ calibrated:1,487487+ is_locked:1, /* used in bcm43xx_phy_{un}lock() */488488+ dyn_tssi_tbl:1; /* used in bcm43xx_phy_init_tssi2dbm_table() */489489+ /* LO Measurement Data.490490+ * Use bcm43xx_get_lopair() to get a value.491491+ */492492+ struct bcm43xx_lopair *_lo_pairs;493493+494494+ /* TSSI to dBm table in use */495495+ const s8 *tssi2dbm;496496+ /* idle TSSI value */497497+ s8 idle_tssi;498498+499499+ /* Values from bcm43xx_calc_loopback_gain() */500500+ u16 loopback_gain[2];501501+502502+ /* PHY lock for core.rev < 3503503+ * This lock is only used by bcm43xx_phy_{un}lock()504504+ */505505+ spinlock_t lock;506506+};507507+508508+509509+struct bcm43xx_radioinfo {510510+ u16 manufact;511511+ u16 version;512512+ u8 revision;513513+514514+ /* Desired TX power in dBm Q5.2 */515515+ u16 txpower_desired;516516+ /* TX Power control values. */517517+ union {518518+ /* B/G PHY */519519+ struct {520520+ u16 baseband_atten;521521+ u16 radio_atten;522522+ u16 txctl1;523523+ u16 txctl2;524524+ };525525+ /* A PHY */526526+ struct {527527+ u16 txpwr_offset;528528+ };529529+ };530530+531531+ /* Current Interference Mitigation mode */532532+ int interfmode;533533+ /* Stack of saved values from the Interference Mitigation code.534534+ * Each value in the stack is layed out as follows:535535+ * bit 0-11: offset536536+ * bit 12-15: register ID537537+ * bit 16-32: value538538+ * register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT539539+ */540540+#define BCM43xx_INTERFSTACK_SIZE 26541541+ u32 interfstack[BCM43xx_INTERFSTACK_SIZE];542542+543543+ /* Saved values from the NRSSI Slope calculation */544544+ s16 nrssi[2];545545+ s32 nrssislope;546546+ /* In memory nrssi lookup table. */547547+ s8 nrssi_lt[64];548548+549549+ /* current channel */550550+ u8 channel;551551+ u8 initial_channel;552552+553553+ u16 lofcal;554554+555555+ u16 initval;556556+557557+ u8 enabled:1;558558+ /* ACI (adjacent channel interference) flags. */559559+ u8 aci_enable:1,560560+ aci_wlan_automatic:1,561561+ aci_hw_rssi:1;562562+};563563+564564+/* Data structures for DMA transmission, per 80211 core. */565565+struct bcm43xx_dma {566566+ struct bcm43xx_dmaring *tx_ring0;567567+ struct bcm43xx_dmaring *tx_ring1;568568+ struct bcm43xx_dmaring *tx_ring2;569569+ struct bcm43xx_dmaring *tx_ring3;570570+ struct bcm43xx_dmaring *rx_ring0;571571+ struct bcm43xx_dmaring *rx_ring1; /* only available on core.rev < 5 */572572+};573573+574574+/* Data structures for PIO transmission, per 80211 core. */575575+struct bcm43xx_pio {576576+ struct bcm43xx_pioqueue *queue0;577577+ struct bcm43xx_pioqueue *queue1;578578+ struct bcm43xx_pioqueue *queue2;579579+ struct bcm43xx_pioqueue *queue3;580580+};581581+582582+#define BCM43xx_MAX_80211_CORES 2583583+584584+#ifdef CONFIG_BCM947XX585585+#define core_offset(bcm) (bcm)->current_core_offset586586+#else587587+#define core_offset(bcm) 0588588+#endif589589+590590+/* Generic information about a core. */591591+struct bcm43xx_coreinfo {592592+ u8 available:1,593593+ enabled:1,594594+ initialized:1;595595+ /** core_id ID number */596596+ u16 id;597597+ /** core_rev revision number */598598+ u8 rev;599599+ /** Index number for _switch_core() */600600+ u8 index;601601+};602602+603603+/* Additional information for each 80211 core. */604604+struct bcm43xx_coreinfo_80211 {605605+ /* PHY device. */606606+ struct bcm43xx_phyinfo phy;607607+ /* Radio device. */608608+ struct bcm43xx_radioinfo radio;609609+ union {610610+ /* DMA context. */611611+ struct bcm43xx_dma dma;612612+ /* PIO context. */613613+ struct bcm43xx_pio pio;614614+ };615615+};616616+617617+/* Context information for a noise calculation (Link Quality). */618618+struct bcm43xx_noise_calculation {619619+ struct bcm43xx_coreinfo *core_at_start;620620+ u8 channel_at_start;621621+ u8 calculation_running:1;622622+ u8 nr_samples;623623+ s8 samples[8][4];624624+};625625+626626+struct bcm43xx_stats {627627+ u8 link_quality;628628+ u8 noise;629629+ struct iw_statistics wstats;630630+ /* Store the last TX/RX times here for updating the leds. */631631+ unsigned long last_tx;632632+ unsigned long last_rx;633633+};634634+635635+struct bcm43xx_key {636636+ u8 enabled:1;637637+ u8 algorithm;638638+};639639+640640+struct bcm43xx_private {641641+ struct bcm43xx_sysfs sysfs;642642+643643+ struct ieee80211_device *ieee;644644+ struct ieee80211softmac_device *softmac;645645+646646+ struct net_device *net_dev;647647+ struct pci_dev *pci_dev;648648+ unsigned int irq;649649+650650+ void __iomem *mmio_addr;651651+ unsigned int mmio_len;652652+653653+ /* Do not use the lock directly. Use the bcm43xx_lock* helper654654+ * functions, to be MMIO-safe. */655655+ spinlock_t _lock;656656+657657+ /* Driver status flags. */658658+ u32 initialized:1, /* init_board() succeed */659659+ was_initialized:1, /* for PCI suspend/resume. */660660+ shutting_down:1, /* free_board() in progress */661661+ __using_pio:1, /* Internal, use bcm43xx_using_pio(). */662662+ bad_frames_preempt:1, /* Use "Bad Frames Preemption" (default off) */663663+ reg124_set_0x4:1, /* Some variable to keep track of IRQ stuff. */664664+ powersaving:1, /* TRUE if we are in PowerSaving mode. FALSE otherwise. */665665+ short_preamble:1, /* TRUE, if short preamble is enabled. */666666+ firmware_norelease:1; /* Do not release the firmware. Used on suspend. */667667+668668+ struct bcm43xx_stats stats;669669+670670+ /* Bus type we are connected to.671671+ * This is currently always BCM43xx_BUSTYPE_PCI672672+ */673673+ u8 bustype;674674+675675+ u16 board_vendor;676676+ u16 board_type;677677+ u16 board_revision;678678+679679+ u16 chip_id;680680+ u8 chip_rev;681681+ u8 chip_package;682682+683683+ struct bcm43xx_sprominfo sprom;684684+#define BCM43xx_NR_LEDS 4685685+ struct bcm43xx_led leds[BCM43xx_NR_LEDS];686686+687687+ /* The currently active core. */688688+ struct bcm43xx_coreinfo *current_core;689689+#ifdef CONFIG_BCM947XX690690+ /** current core memory offset */691691+ u32 current_core_offset;692692+#endif693693+ struct bcm43xx_coreinfo *active_80211_core;694694+ /* coreinfo structs for all possible cores follow.695695+ * Note that a core might not exist.696696+ * So check the coreinfo flags before using it.697697+ */698698+ struct bcm43xx_coreinfo core_chipcommon;699699+ struct bcm43xx_coreinfo core_pci;700700+ struct bcm43xx_coreinfo core_80211[ BCM43xx_MAX_80211_CORES ];701701+ /* Additional information, specific to the 80211 cores. */702702+ struct bcm43xx_coreinfo_80211 core_80211_ext[ BCM43xx_MAX_80211_CORES ];703703+ /* Index of the current 80211 core. If current_core is not704704+ * an 80211 core, this is -1.705705+ */706706+ int current_80211_core_idx;707707+ /* Number of available 80211 cores. */708708+ int nr_80211_available;709709+710710+ u32 chipcommon_capabilities;711711+712712+ /* Reason code of the last interrupt. */713713+ u32 irq_reason;714714+ u32 dma_reason[4];715715+ /* saved irq enable/disable state bitfield. */716716+ u32 irq_savedstate;717717+ /* Link Quality calculation context. */718718+ struct bcm43xx_noise_calculation noisecalc;719719+720720+ /* Threshold values. */721721+ //TODO: The RTS thr has to be _used_. Currently, it is only set via WX.722722+ u32 rts_threshold;723723+724724+ /* Interrupt Service Routine tasklet (bottom-half) */725725+ struct tasklet_struct isr_tasklet;726726+727727+ /* Periodic tasks */728728+ struct timer_list periodic_tasks;729729+ unsigned int periodic_state;730730+731731+ struct work_struct restart_work;732732+733733+ /* Informational stuff. */734734+ char nick[IW_ESSID_MAX_SIZE + 1];735735+736736+ /* encryption/decryption */737737+ u16 security_offset;738738+ struct bcm43xx_key key[54];739739+ u8 default_key_idx;740740+741741+ /* Firmware. */742742+ const struct firmware *ucode;743743+ const struct firmware *pcm;744744+ const struct firmware *initvals0;745745+ const struct firmware *initvals1;746746+747747+ /* Debugging stuff follows. */748748+#ifdef CONFIG_BCM43XX_DEBUG749749+ struct bcm43xx_dfsentry *dfsentry;750750+#endif751751+};752752+753753+/* bcm43xx_(un)lock() protect struct bcm43xx_private.754754+ * Note that _NO_ MMIO writes are allowed. If you want to755755+ * write to the device through MMIO in the critical section, use756756+ * the *_mmio lock functions.757757+ * MMIO read-access is allowed, though.758758+ */759759+#define bcm43xx_lock(bcm, flags) spin_lock_irqsave(&(bcm)->_lock, flags)760760+#define bcm43xx_unlock(bcm, flags) spin_unlock_irqrestore(&(bcm)->_lock, flags)761761+/* bcm43xx_(un)lock_mmio() protect struct bcm43xx_private and MMIO.762762+ * MMIO write-access to the device is allowed.763763+ * All MMIO writes are flushed on unlock, so it is guaranteed to not764764+ * interfere with other threads writing MMIO registers.765765+ */766766+#define bcm43xx_lock_mmio(bcm, flags) bcm43xx_lock(bcm, flags)767767+#define bcm43xx_unlock_mmio(bcm, flags) do { mmiowb(); bcm43xx_unlock(bcm, flags); } while (0)768768+769769+static inline770770+struct bcm43xx_private * bcm43xx_priv(struct net_device *dev)771771+{772772+ return ieee80211softmac_priv(dev);773773+}774774+775775+776776+/* Helper function, which returns a boolean.777777+ * TRUE, if PIO is used; FALSE, if DMA is used.778778+ */779779+#if defined(CONFIG_BCM43XX_DMA) && defined(CONFIG_BCM43XX_PIO)780780+static inline781781+int bcm43xx_using_pio(struct bcm43xx_private *bcm)782782+{783783+ return bcm->__using_pio;784784+}785785+#elif defined(CONFIG_BCM43XX_DMA)786786+static inline787787+int bcm43xx_using_pio(struct bcm43xx_private *bcm)788788+{789789+ return 0;790790+}791791+#elif defined(CONFIG_BCM43XX_PIO)792792+static inline793793+int bcm43xx_using_pio(struct bcm43xx_private *bcm)794794+{795795+ return 1;796796+}797797+#else798798+# error "Using neither DMA nor PIO? Confused..."799799+#endif800800+801801+/* Helper functions to access data structures private to the 80211 cores.802802+ * Note that we _must_ have an 80211 core mapped when calling803803+ * any of these functions.804804+ */805805+static inline806806+struct bcm43xx_pio * bcm43xx_current_pio(struct bcm43xx_private *bcm)807807+{808808+ assert(bcm43xx_using_pio(bcm));809809+ assert(bcm->current_80211_core_idx >= 0);810810+ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);811811+ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].pio);812812+}813813+static inline814814+struct bcm43xx_dma * bcm43xx_current_dma(struct bcm43xx_private *bcm)815815+{816816+ assert(!bcm43xx_using_pio(bcm));817817+ assert(bcm->current_80211_core_idx >= 0);818818+ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);819819+ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].dma);820820+}821821+static inline822822+struct bcm43xx_phyinfo * bcm43xx_current_phy(struct bcm43xx_private *bcm)823823+{824824+ assert(bcm->current_80211_core_idx >= 0);825825+ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);826826+ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].phy);827827+}828828+static inline829829+struct bcm43xx_radioinfo * bcm43xx_current_radio(struct bcm43xx_private *bcm)830830+{831831+ assert(bcm->current_80211_core_idx >= 0);832832+ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);833833+ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].radio);834834+}835835+836836+/* Are we running in init_board() context? */837837+static inline838838+int bcm43xx_is_initializing(struct bcm43xx_private *bcm)839839+{840840+ if (bcm->initialized)841841+ return 0;842842+ if (bcm->shutting_down)843843+ return 0;844844+ return 1;845845+}846846+847847+static inline848848+struct bcm43xx_lopair * bcm43xx_get_lopair(struct bcm43xx_phyinfo *phy,849849+ u16 radio_attenuation,850850+ u16 baseband_attenuation)851851+{852852+ return phy->_lo_pairs + (radio_attenuation + 14 * (baseband_attenuation / 2));853853+}854854+855855+856856+static inline857857+u16 bcm43xx_read16(struct bcm43xx_private *bcm, u16 offset)858858+{859859+ return ioread16(bcm->mmio_addr + core_offset(bcm) + offset);860860+}861861+862862+static inline863863+void bcm43xx_write16(struct bcm43xx_private *bcm, u16 offset, u16 value)864864+{865865+ iowrite16(value, bcm->mmio_addr + core_offset(bcm) + offset);866866+}867867+868868+static inline869869+u32 bcm43xx_read32(struct bcm43xx_private *bcm, u16 offset)870870+{871871+ return ioread32(bcm->mmio_addr + core_offset(bcm) + offset);872872+}873873+874874+static inline875875+void bcm43xx_write32(struct bcm43xx_private *bcm, u16 offset, u32 value)876876+{877877+ iowrite32(value, bcm->mmio_addr + core_offset(bcm) + offset);878878+}879879+880880+static inline881881+int bcm43xx_pci_read_config16(struct bcm43xx_private *bcm, int offset, u16 *value)882882+{883883+ return pci_read_config_word(bcm->pci_dev, offset, value);884884+}885885+886886+static inline887887+int bcm43xx_pci_read_config32(struct bcm43xx_private *bcm, int offset, u32 *value)888888+{889889+ return pci_read_config_dword(bcm->pci_dev, offset, value);890890+}891891+892892+static inline893893+int bcm43xx_pci_write_config16(struct bcm43xx_private *bcm, int offset, u16 value)894894+{895895+ return pci_write_config_word(bcm->pci_dev, offset, value);896896+}897897+898898+static inline899899+int bcm43xx_pci_write_config32(struct bcm43xx_private *bcm, int offset, u32 value)900900+{901901+ return pci_write_config_dword(bcm->pci_dev, offset, value);902902+}903903+904904+/** Limit a value between two limits */905905+#ifdef limit_value906906+# undef limit_value907907+#endif908908+#define limit_value(value, min, max) \909909+ ({ \910910+ typeof(value) __value = (value); \911911+ typeof(value) __min = (min); \912912+ typeof(value) __max = (max); \913913+ if (__value < __min) \914914+ __value = __min; \915915+ else if (__value > __max) \916916+ __value = __max; \917917+ __value; \918918+ })919919+920920+/** Helpers to print MAC addresses. */921921+#define BCM43xx_MACFMT "%02x:%02x:%02x:%02x:%02x:%02x"922922+#define BCM43xx_MACARG(x) ((u8*)(x))[0], ((u8*)(x))[1], \923923+ ((u8*)(x))[2], ((u8*)(x))[3], \924924+ ((u8*)(x))[4], ((u8*)(x))[5]925925+926926+#endif /* BCM43xx_H_ */
+499
drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ debugfs driver debugging code66+77+ Copyright (c) 2005 Michael Buesch <mbuesch@freenet.de>88+99+ This program is free software; you can redistribute it and/or modify1010+ it under the terms of the GNU General Public License as published by1111+ the Free Software Foundation; either version 2 of the License, or1212+ (at your option) any later version.1313+1414+ This program is distributed in the hope that it will be useful,1515+ but WITHOUT ANY WARRANTY; without even the implied warranty of1616+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1717+ GNU General Public License for more details.1818+1919+ You should have received a copy of the GNU General Public License2020+ along with this program; see the file COPYING. If not, write to2121+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2222+ Boston, MA 02110-1301, USA.2323+2424+*/2525+2626+2727+2828+#include <linux/fs.h>2929+#include <linux/debugfs.h>3030+#include <linux/slab.h>3131+#include <linux/netdevice.h>3232+#include <linux/pci.h>3333+#include <asm/io.h>3434+3535+#include "bcm43xx.h"3636+#include "bcm43xx_main.h"3737+#include "bcm43xx_debugfs.h"3838+#include "bcm43xx_dma.h"3939+#include "bcm43xx_pio.h"4040+#include "bcm43xx_xmit.h"4141+4242+#define REALLY_BIG_BUFFER_SIZE (1024*256)4343+4444+static struct bcm43xx_debugfs fs;4545+static char really_big_buffer[REALLY_BIG_BUFFER_SIZE];4646+static DECLARE_MUTEX(big_buffer_sem);4747+4848+4949+static ssize_t write_file_dummy(struct file *file, const char __user *buf,5050+ size_t count, loff_t *ppos)5151+{5252+ return count;5353+}5454+5555+static int open_file_generic(struct inode *inode, struct file *file)5656+{5757+ file->private_data = inode->u.generic_ip;5858+ return 0;5959+}6060+6161+#define fappend(fmt, x...) pos += snprintf(buf + pos, len - pos, fmt , ##x)6262+6363+static ssize_t devinfo_read_file(struct file *file, char __user *userbuf,6464+ size_t count, loff_t *ppos)6565+{6666+ const size_t len = REALLY_BIG_BUFFER_SIZE;6767+6868+ struct bcm43xx_private *bcm = file->private_data;6969+ char *buf = really_big_buffer;7070+ size_t pos = 0;7171+ ssize_t res;7272+ struct net_device *net_dev;7373+ struct pci_dev *pci_dev;7474+ unsigned long flags;7575+ u16 tmp16;7676+ int i;7777+7878+ down(&big_buffer_sem);7979+8080+ bcm43xx_lock_mmio(bcm, flags);8181+ if (!bcm->initialized) {8282+ fappend("Board not initialized.\n");8383+ goto out;8484+ }8585+ net_dev = bcm->net_dev;8686+ pci_dev = bcm->pci_dev;8787+8888+ /* This is where the information is written to the "devinfo" file */8989+ fappend("*** %s devinfo ***\n", net_dev->name);9090+ fappend("vendor: 0x%04x device: 0x%04x\n",9191+ pci_dev->vendor, pci_dev->device);9292+ fappend("subsystem_vendor: 0x%04x subsystem_device: 0x%04x\n",9393+ pci_dev->subsystem_vendor, pci_dev->subsystem_device);9494+ fappend("IRQ: %d\n", bcm->irq);9595+ fappend("mmio_addr: 0x%p mmio_len: %u\n", bcm->mmio_addr, bcm->mmio_len);9696+ fappend("chip_id: 0x%04x chip_rev: 0x%02x\n", bcm->chip_id, bcm->chip_rev);9797+ if ((bcm->core_80211[0].rev >= 3) && (bcm43xx_read32(bcm, 0x0158) & (1 << 16)))9898+ fappend("Radio disabled by hardware!\n");9999+ if ((bcm->core_80211[0].rev < 3) && !(bcm43xx_read16(bcm, 0x049A) & (1 << 4)))100100+ fappend("Radio disabled by hardware!\n");101101+ fappend("board_vendor: 0x%04x board_type: 0x%04x\n", bcm->board_vendor,102102+ bcm->board_type);103103+104104+ fappend("\nCores:\n");105105+#define fappend_core(name, info) fappend("core \"" name "\" %s, %s, id: 0x%04x, " \106106+ "rev: 0x%02x, index: 0x%02x\n", \107107+ (info).available \108108+ ? "available" : "nonavailable", \109109+ (info).enabled \110110+ ? "enabled" : "disabled", \111111+ (info).id, (info).rev, (info).index)112112+ fappend_core("CHIPCOMMON", bcm->core_chipcommon);113113+ fappend_core("PCI", bcm->core_pci);114114+ fappend_core("first 80211", bcm->core_80211[0]);115115+ fappend_core("second 80211", bcm->core_80211[1]);116116+#undef fappend_core117117+ tmp16 = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);118118+ fappend("LEDs: ");119119+ for (i = 0; i < BCM43xx_NR_LEDS; i++)120120+ fappend("%d ", !!(tmp16 & (1 << i)));121121+ fappend("\n");122122+123123+out:124124+ bcm43xx_unlock_mmio(bcm, flags);125125+ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);126126+ up(&big_buffer_sem);127127+ return res;128128+}129129+130130+static ssize_t drvinfo_read_file(struct file *file, char __user *userbuf,131131+ size_t count, loff_t *ppos)132132+{133133+ const size_t len = REALLY_BIG_BUFFER_SIZE;134134+135135+ char *buf = really_big_buffer;136136+ size_t pos = 0;137137+ ssize_t res;138138+139139+ down(&big_buffer_sem);140140+141141+ /* This is where the information is written to the "driver" file */142142+ fappend(KBUILD_MODNAME " driver\n");143143+ fappend("Compiled at: %s %s\n", __DATE__, __TIME__);144144+145145+ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);146146+ up(&big_buffer_sem);147147+ return res;148148+}149149+150150+static ssize_t spromdump_read_file(struct file *file, char __user *userbuf,151151+ size_t count, loff_t *ppos)152152+{153153+ const size_t len = REALLY_BIG_BUFFER_SIZE;154154+155155+ struct bcm43xx_private *bcm = file->private_data;156156+ char *buf = really_big_buffer;157157+ size_t pos = 0;158158+ ssize_t res;159159+ unsigned long flags;160160+161161+ down(&big_buffer_sem);162162+ bcm43xx_lock_mmio(bcm, flags);163163+ if (!bcm->initialized) {164164+ fappend("Board not initialized.\n");165165+ goto out;166166+ }167167+168168+ /* This is where the information is written to the "sprom_dump" file */169169+ fappend("boardflags: 0x%04x\n", bcm->sprom.boardflags);170170+171171+out:172172+ bcm43xx_unlock_mmio(bcm, flags);173173+ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);174174+ up(&big_buffer_sem);175175+ return res;176176+}177177+178178+static ssize_t tsf_read_file(struct file *file, char __user *userbuf,179179+ size_t count, loff_t *ppos)180180+{181181+ const size_t len = REALLY_BIG_BUFFER_SIZE;182182+183183+ struct bcm43xx_private *bcm = file->private_data;184184+ char *buf = really_big_buffer;185185+ size_t pos = 0;186186+ ssize_t res;187187+ unsigned long flags;188188+ u64 tsf;189189+190190+ down(&big_buffer_sem);191191+ bcm43xx_lock_mmio(bcm, flags);192192+ if (!bcm->initialized) {193193+ fappend("Board not initialized.\n");194194+ goto out;195195+ }196196+ bcm43xx_tsf_read(bcm, &tsf);197197+ fappend("0x%08x%08x\n",198198+ (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32),199199+ (unsigned int)(tsf & 0xFFFFFFFFULL));200200+201201+out:202202+ bcm43xx_unlock_mmio(bcm, flags);203203+ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);204204+ up(&big_buffer_sem);205205+ return res;206206+}207207+208208+static ssize_t tsf_write_file(struct file *file, const char __user *user_buf,209209+ size_t count, loff_t *ppos)210210+{211211+ struct bcm43xx_private *bcm = file->private_data;212212+ char *buf = really_big_buffer;213213+ ssize_t buf_size;214214+ ssize_t res;215215+ unsigned long flags;216216+ u64 tsf;217217+218218+ buf_size = min(count, sizeof (really_big_buffer) - 1);219219+ down(&big_buffer_sem);220220+ if (copy_from_user(buf, user_buf, buf_size)) {221221+ res = -EFAULT;222222+ goto out_up;223223+ }224224+ bcm43xx_lock_mmio(bcm, flags);225225+ if (!bcm->initialized) {226226+ printk(KERN_INFO PFX "debugfs: Board not initialized.\n");227227+ res = -EFAULT;228228+ goto out_unlock;229229+ }230230+ if (sscanf(buf, "%lli", &tsf) != 1) {231231+ printk(KERN_INFO PFX "debugfs: invalid values for \"tsf\"\n");232232+ res = -EINVAL;233233+ goto out_unlock;234234+ }235235+ bcm43xx_tsf_write(bcm, tsf);236236+ res = buf_size;237237+238238+out_unlock:239239+ bcm43xx_unlock_mmio(bcm, flags);240240+out_up:241241+ up(&big_buffer_sem);242242+ return res;243243+}244244+245245+static ssize_t txstat_read_file(struct file *file, char __user *userbuf,246246+ size_t count, loff_t *ppos)247247+{248248+ const size_t len = REALLY_BIG_BUFFER_SIZE;249249+250250+ struct bcm43xx_private *bcm = file->private_data;251251+ char *buf = really_big_buffer;252252+ size_t pos = 0;253253+ ssize_t res;254254+ unsigned long flags;255255+ struct bcm43xx_dfsentry *e;256256+ struct bcm43xx_xmitstatus *status;257257+ int i, cnt, j = 0;258258+259259+ down(&big_buffer_sem);260260+ bcm43xx_lock(bcm, flags);261261+262262+ fappend("Last %d logged xmitstatus blobs (Latest first):\n\n",263263+ BCM43xx_NR_LOGGED_XMITSTATUS);264264+ e = bcm->dfsentry;265265+ if (e->xmitstatus_printing == 0) {266266+ /* At the beginning, make a copy of all data to avoid267267+ * concurrency, as this function is called multiple268268+ * times for big logs. Without copying, the data might269269+ * change between reads. This would result in total trash.270270+ */271271+ e->xmitstatus_printing = 1;272272+ e->saved_xmitstatus_ptr = e->xmitstatus_ptr;273273+ e->saved_xmitstatus_cnt = e->xmitstatus_cnt;274274+ memcpy(e->xmitstatus_print_buffer, e->xmitstatus_buffer,275275+ BCM43xx_NR_LOGGED_XMITSTATUS * sizeof(*(e->xmitstatus_buffer)));276276+ }277277+ i = e->saved_xmitstatus_ptr - 1;278278+ if (i < 0)279279+ i = BCM43xx_NR_LOGGED_XMITSTATUS - 1;280280+ cnt = e->saved_xmitstatus_cnt;281281+ while (cnt) {282282+ status = e->xmitstatus_print_buffer + i;283283+ fappend("0x%02x: cookie: 0x%04x, flags: 0x%02x, "284284+ "cnt1: 0x%02x, cnt2: 0x%02x, seq: 0x%04x, "285285+ "unk: 0x%04x\n", j,286286+ status->cookie, status->flags,287287+ status->cnt1, status->cnt2, status->seq,288288+ status->unknown);289289+ j++;290290+ cnt--;291291+ i--;292292+ if (i < 0)293293+ i = BCM43xx_NR_LOGGED_XMITSTATUS - 1;294294+ }295295+296296+ bcm43xx_unlock(bcm, flags);297297+ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);298298+ bcm43xx_lock(bcm, flags);299299+ if (*ppos == pos) {300300+ /* Done. Drop the copied data. */301301+ e->xmitstatus_printing = 0;302302+ }303303+ bcm43xx_unlock(bcm, flags);304304+ up(&big_buffer_sem);305305+ return res;306306+}307307+308308+#undef fappend309309+310310+311311+static struct file_operations devinfo_fops = {312312+ .read = devinfo_read_file,313313+ .write = write_file_dummy,314314+ .open = open_file_generic,315315+};316316+317317+static struct file_operations spromdump_fops = {318318+ .read = spromdump_read_file,319319+ .write = write_file_dummy,320320+ .open = open_file_generic,321321+};322322+323323+static struct file_operations drvinfo_fops = {324324+ .read = drvinfo_read_file,325325+ .write = write_file_dummy,326326+ .open = open_file_generic,327327+};328328+329329+static struct file_operations tsf_fops = {330330+ .read = tsf_read_file,331331+ .write = tsf_write_file,332332+ .open = open_file_generic,333333+};334334+335335+static struct file_operations txstat_fops = {336336+ .read = txstat_read_file,337337+ .write = write_file_dummy,338338+ .open = open_file_generic,339339+};340340+341341+342342+void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm)343343+{344344+ struct bcm43xx_dfsentry *e;345345+ char devdir[IFNAMSIZ];346346+347347+ assert(bcm);348348+ e = kzalloc(sizeof(*e), GFP_KERNEL);349349+ if (!e) {350350+ printk(KERN_ERR PFX "out of memory\n");351351+ return;352352+ }353353+ e->bcm = bcm;354354+ e->xmitstatus_buffer = kzalloc(BCM43xx_NR_LOGGED_XMITSTATUS355355+ * sizeof(*(e->xmitstatus_buffer)),356356+ GFP_KERNEL);357357+ if (!e->xmitstatus_buffer) {358358+ printk(KERN_ERR PFX "out of memory\n");359359+ kfree(e);360360+ return;361361+ }362362+ e->xmitstatus_print_buffer = kzalloc(BCM43xx_NR_LOGGED_XMITSTATUS363363+ * sizeof(*(e->xmitstatus_buffer)),364364+ GFP_KERNEL);365365+ if (!e->xmitstatus_print_buffer) {366366+ printk(KERN_ERR PFX "out of memory\n");367367+ kfree(e);368368+ return;369369+ }370370+371371+372372+ bcm->dfsentry = e;373373+374374+ strncpy(devdir, bcm->net_dev->name, ARRAY_SIZE(devdir));375375+ e->subdir = debugfs_create_dir(devdir, fs.root);376376+ e->dentry_devinfo = debugfs_create_file("devinfo", 0444, e->subdir,377377+ bcm, &devinfo_fops);378378+ if (!e->dentry_devinfo)379379+ printk(KERN_ERR PFX "debugfs: creating \"devinfo\" for \"%s\" failed!\n", devdir);380380+ e->dentry_spromdump = debugfs_create_file("sprom_dump", 0444, e->subdir,381381+ bcm, &spromdump_fops);382382+ if (!e->dentry_spromdump)383383+ printk(KERN_ERR PFX "debugfs: creating \"sprom_dump\" for \"%s\" failed!\n", devdir);384384+ e->dentry_tsf = debugfs_create_file("tsf", 0666, e->subdir,385385+ bcm, &tsf_fops);386386+ if (!e->dentry_tsf)387387+ printk(KERN_ERR PFX "debugfs: creating \"tsf\" for \"%s\" failed!\n", devdir);388388+ e->dentry_txstat = debugfs_create_file("tx_status", 0444, e->subdir,389389+ bcm, &txstat_fops);390390+ if (!e->dentry_txstat)391391+ printk(KERN_ERR PFX "debugfs: creating \"tx_status\" for \"%s\" failed!\n", devdir);392392+}393393+394394+void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm)395395+{396396+ struct bcm43xx_dfsentry *e;397397+398398+ if (!bcm)399399+ return;400400+401401+ e = bcm->dfsentry;402402+ assert(e);403403+ debugfs_remove(e->dentry_spromdump);404404+ debugfs_remove(e->dentry_devinfo);405405+ debugfs_remove(e->dentry_tsf);406406+ debugfs_remove(e->dentry_txstat);407407+ debugfs_remove(e->subdir);408408+ kfree(e->xmitstatus_buffer);409409+ kfree(e->xmitstatus_print_buffer);410410+ kfree(e);411411+}412412+413413+void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm,414414+ struct bcm43xx_xmitstatus *status)415415+{416416+ struct bcm43xx_dfsentry *e;417417+ struct bcm43xx_xmitstatus *savedstatus;418418+419419+ /* This is protected by bcm->_lock */420420+ e = bcm->dfsentry;421421+ assert(e);422422+ savedstatus = e->xmitstatus_buffer + e->xmitstatus_ptr;423423+ memcpy(savedstatus, status, sizeof(*status));424424+ e->xmitstatus_ptr++;425425+ if (e->xmitstatus_ptr >= BCM43xx_NR_LOGGED_XMITSTATUS)426426+ e->xmitstatus_ptr = 0;427427+ if (e->xmitstatus_cnt < BCM43xx_NR_LOGGED_XMITSTATUS)428428+ e->xmitstatus_cnt++;429429+}430430+431431+void bcm43xx_debugfs_init(void)432432+{433433+ memset(&fs, 0, sizeof(fs));434434+ fs.root = debugfs_create_dir(KBUILD_MODNAME, NULL);435435+ if (!fs.root)436436+ printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "\" subdir failed!\n");437437+ fs.dentry_driverinfo = debugfs_create_file("driver", 0444, fs.root, NULL, &drvinfo_fops);438438+ if (!fs.dentry_driverinfo)439439+ printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "/driver\" failed!\n");440440+}441441+442442+void bcm43xx_debugfs_exit(void)443443+{444444+ debugfs_remove(fs.dentry_driverinfo);445445+ debugfs_remove(fs.root);446446+}447447+448448+void bcm43xx_printk_dump(const char *data,449449+ size_t size,450450+ const char *description)451451+{452452+ size_t i;453453+ char c;454454+455455+ printk(KERN_INFO PFX "Data dump (%s, %u bytes):",456456+ description, size);457457+ for (i = 0; i < size; i++) {458458+ c = data[i];459459+ if (i % 8 == 0)460460+ printk("\n" KERN_INFO PFX "0x%08x: 0x%02x, ", i, c & 0xff);461461+ else462462+ printk("0x%02x, ", c & 0xff);463463+ }464464+ printk("\n");465465+}466466+467467+void bcm43xx_printk_bitdump(const unsigned char *data,468468+ size_t bytes, int msb_to_lsb,469469+ const char *description)470470+{471471+ size_t i;472472+ int j;473473+ const unsigned char *d;474474+475475+ printk(KERN_INFO PFX "*** Bitdump (%s, %u bytes, %s) ***",476476+ description, bytes, msb_to_lsb ? "MSB to LSB" : "LSB to MSB");477477+ for (i = 0; i < bytes; i++) {478478+ d = data + i;479479+ if (i % 8 == 0)480480+ printk("\n" KERN_INFO PFX "0x%08x: ", i);481481+ if (msb_to_lsb) {482482+ for (j = 7; j >= 0; j--) {483483+ if (*d & (1 << j))484484+ printk("1");485485+ else486486+ printk("0");487487+ }488488+ } else {489489+ for (j = 0; j < 8; j++) {490490+ if (*d & (1 << j))491491+ printk("1");492492+ else493493+ printk("0");494494+ }495495+ }496496+ printk(" ");497497+ }498498+ printk("\n");499499+}
+117
drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h
···11+#ifndef BCM43xx_DEBUGFS_H_22+#define BCM43xx_DEBUGFS_H_33+44+struct bcm43xx_private;55+struct bcm43xx_xmitstatus;66+77+#ifdef CONFIG_BCM43XX_DEBUG88+99+#include <linux/list.h>1010+#include <asm/semaphore.h>1111+1212+struct dentry;1313+1414+/* limited by the size of the "really_big_buffer" */1515+#define BCM43xx_NR_LOGGED_XMITSTATUS 1001616+1717+struct bcm43xx_dfsentry {1818+ struct dentry *subdir;1919+ struct dentry *dentry_devinfo;2020+ struct dentry *dentry_spromdump;2121+ struct dentry *dentry_tsf;2222+ struct dentry *dentry_txstat;2323+2424+ struct bcm43xx_private *bcm;2525+2626+ /* saved xmitstatus. */2727+ struct bcm43xx_xmitstatus *xmitstatus_buffer;2828+ int xmitstatus_ptr;2929+ int xmitstatus_cnt;3030+ /* We need a seperate buffer while printing to avoid3131+ * concurrency issues. (New xmitstatus can arrive3232+ * while we are printing).3333+ */3434+ struct bcm43xx_xmitstatus *xmitstatus_print_buffer;3535+ int saved_xmitstatus_ptr;3636+ int saved_xmitstatus_cnt;3737+ int xmitstatus_printing;3838+};3939+4040+struct bcm43xx_debugfs {4141+ struct dentry *root;4242+ struct dentry *dentry_driverinfo;4343+};4444+4545+void bcm43xx_debugfs_init(void);4646+void bcm43xx_debugfs_exit(void);4747+void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm);4848+void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm);4949+void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm,5050+ struct bcm43xx_xmitstatus *status);5151+5252+/* Debug helper: Dump binary data through printk. */5353+void bcm43xx_printk_dump(const char *data,5454+ size_t size,5555+ const char *description);5656+/* Debug helper: Dump bitwise binary data through printk. */5757+void bcm43xx_printk_bitdump(const unsigned char *data,5858+ size_t bytes, int msb_to_lsb,5959+ const char *description);6060+#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) \6161+ do { \6262+ bcm43xx_printk_bitdump((const unsigned char *)(pointer), \6363+ sizeof(*(pointer)), \6464+ (msb_to_lsb), \6565+ (description)); \6666+ } while (0)6767+6868+#else /* CONFIG_BCM43XX_DEBUG*/6969+7070+static inline7171+void bcm43xx_debugfs_init(void) { }7272+static inline7373+void bcm43xx_debugfs_exit(void) { }7474+static inline7575+void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm) { }7676+static inline7777+void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm) { }7878+static inline7979+void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm,8080+ struct bcm43xx_xmitstatus *status) { }8181+8282+static inline8383+void bcm43xx_printk_dump(const char *data,8484+ size_t size,8585+ const char *description)8686+{8787+}8888+static inline8989+void bcm43xx_printk_bitdump(const unsigned char *data,9090+ size_t bytes, int msb_to_lsb,9191+ const char *description)9292+{9393+}9494+#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) do { /* nothing */ } while (0)9595+9696+#endif /* CONFIG_BCM43XX_DEBUG*/9797+9898+/* Ugly helper macros to make incomplete code more verbose on runtime */9999+#ifdef TODO100100+# undef TODO101101+#endif102102+#define TODO() \103103+ do { \104104+ printk(KERN_INFO PFX "TODO: Incomplete code in %s() at %s:%d\n", \105105+ __FUNCTION__, __FILE__, __LINE__); \106106+ } while (0)107107+108108+#ifdef FIXME109109+# undef FIXME110110+#endif111111+#define FIXME() \112112+ do { \113113+ printk(KERN_INFO PFX "FIXME: Possibly broken code in %s() at %s:%d\n", \114114+ __FUNCTION__, __FILE__, __LINE__); \115115+ } while (0)116116+117117+#endif /* BCM43xx_DEBUGFS_H_ */
+968
drivers/net/wireless/bcm43xx/bcm43xx_dma.c
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ DMA ringbuffer and descriptor allocation/management66+77+ Copyright (c) 2005 Michael Buesch <mbuesch@freenet.de>88+99+ Some code in this file is derived from the b44.c driver1010+ Copyright (C) 2002 David S. Miller1111+ Copyright (C) Pekka Pietikainen1212+1313+ This program is free software; you can redistribute it and/or modify1414+ it under the terms of the GNU General Public License as published by1515+ the Free Software Foundation; either version 2 of the License, or1616+ (at your option) any later version.1717+1818+ This program is distributed in the hope that it will be useful,1919+ but WITHOUT ANY WARRANTY; without even the implied warranty of2020+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the2121+ GNU General Public License for more details.2222+2323+ You should have received a copy of the GNU General Public License2424+ along with this program; see the file COPYING. If not, write to2525+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2626+ Boston, MA 02110-1301, USA.2727+2828+*/2929+3030+#include "bcm43xx.h"3131+#include "bcm43xx_dma.h"3232+#include "bcm43xx_main.h"3333+#include "bcm43xx_debugfs.h"3434+#include "bcm43xx_power.h"3535+#include "bcm43xx_xmit.h"3636+3737+#include <linux/dma-mapping.h>3838+#include <linux/pci.h>3939+#include <linux/delay.h>4040+#include <linux/skbuff.h>4141+4242+4343+static inline int free_slots(struct bcm43xx_dmaring *ring)4444+{4545+ return (ring->nr_slots - ring->used_slots);4646+}4747+4848+static inline int next_slot(struct bcm43xx_dmaring *ring, int slot)4949+{5050+ assert(slot >= -1 && slot <= ring->nr_slots - 1);5151+ if (slot == ring->nr_slots - 1)5252+ return 0;5353+ return slot + 1;5454+}5555+5656+static inline int prev_slot(struct bcm43xx_dmaring *ring, int slot)5757+{5858+ assert(slot >= 0 && slot <= ring->nr_slots - 1);5959+ if (slot == 0)6060+ return ring->nr_slots - 1;6161+ return slot - 1;6262+}6363+6464+/* Request a slot for usage. */6565+static inline6666+int request_slot(struct bcm43xx_dmaring *ring)6767+{6868+ int slot;6969+7070+ assert(ring->tx);7171+ assert(!ring->suspended);7272+ assert(free_slots(ring) != 0);7373+7474+ slot = next_slot(ring, ring->current_slot);7575+ ring->current_slot = slot;7676+ ring->used_slots++;7777+7878+ /* Check the number of available slots and suspend TX,7979+ * if we are running low on free slots.8080+ */8181+ if (unlikely(free_slots(ring) < ring->suspend_mark)) {8282+ netif_stop_queue(ring->bcm->net_dev);8383+ ring->suspended = 1;8484+ }8585+#ifdef CONFIG_BCM43XX_DEBUG8686+ if (ring->used_slots > ring->max_used_slots)8787+ ring->max_used_slots = ring->used_slots;8888+#endif /* CONFIG_BCM43XX_DEBUG*/8989+9090+ return slot;9191+}9292+9393+/* Return a slot to the free slots. */9494+static inline9595+void return_slot(struct bcm43xx_dmaring *ring, int slot)9696+{9797+ assert(ring->tx);9898+9999+ ring->used_slots--;100100+101101+ /* Check if TX is suspended and check if we have102102+ * enough free slots to resume it again.103103+ */104104+ if (unlikely(ring->suspended)) {105105+ if (free_slots(ring) >= ring->resume_mark) {106106+ ring->suspended = 0;107107+ netif_wake_queue(ring->bcm->net_dev);108108+ }109109+ }110110+}111111+112112+static inline113113+dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring,114114+ unsigned char *buf,115115+ size_t len,116116+ int tx)117117+{118118+ dma_addr_t dmaaddr;119119+120120+ if (tx) {121121+ dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev,122122+ buf, len,123123+ DMA_TO_DEVICE);124124+ } else {125125+ dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev,126126+ buf, len,127127+ DMA_FROM_DEVICE);128128+ }129129+130130+ return dmaaddr;131131+}132132+133133+static inline134134+void unmap_descbuffer(struct bcm43xx_dmaring *ring,135135+ dma_addr_t addr,136136+ size_t len,137137+ int tx)138138+{139139+ if (tx) {140140+ dma_unmap_single(&ring->bcm->pci_dev->dev,141141+ addr, len,142142+ DMA_TO_DEVICE);143143+ } else {144144+ dma_unmap_single(&ring->bcm->pci_dev->dev,145145+ addr, len,146146+ DMA_FROM_DEVICE);147147+ }148148+}149149+150150+static inline151151+void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring,152152+ dma_addr_t addr,153153+ size_t len)154154+{155155+ assert(!ring->tx);156156+157157+ dma_sync_single_for_cpu(&ring->bcm->pci_dev->dev,158158+ addr, len, DMA_FROM_DEVICE);159159+}160160+161161+static inline162162+void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring,163163+ dma_addr_t addr,164164+ size_t len)165165+{166166+ assert(!ring->tx);167167+168168+ dma_sync_single_for_device(&ring->bcm->pci_dev->dev,169169+ addr, len, DMA_FROM_DEVICE);170170+}171171+172172+/* Unmap and free a descriptor buffer. */173173+static inline174174+void free_descriptor_buffer(struct bcm43xx_dmaring *ring,175175+ struct bcm43xx_dmadesc *desc,176176+ struct bcm43xx_dmadesc_meta *meta,177177+ int irq_context)178178+{179179+ assert(meta->skb);180180+ if (irq_context)181181+ dev_kfree_skb_irq(meta->skb);182182+ else183183+ dev_kfree_skb(meta->skb);184184+ meta->skb = NULL;185185+}186186+187187+static int alloc_ringmemory(struct bcm43xx_dmaring *ring)188188+{189189+ struct device *dev = &(ring->bcm->pci_dev->dev);190190+191191+ ring->vbase = dma_alloc_coherent(dev, BCM43xx_DMA_RINGMEMSIZE,192192+ &(ring->dmabase), GFP_KERNEL);193193+ if (!ring->vbase) {194194+ printk(KERN_ERR PFX "DMA ringmemory allocation failed\n");195195+ return -ENOMEM;196196+ }197197+ if (ring->dmabase + BCM43xx_DMA_RINGMEMSIZE > BCM43xx_DMA_BUSADDRMAX) {198198+ printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA RINGMEMORY >1G "199199+ "(0x%08x, len: %lu)\n",200200+ ring->dmabase, BCM43xx_DMA_RINGMEMSIZE);201201+ dma_free_coherent(dev, BCM43xx_DMA_RINGMEMSIZE,202202+ ring->vbase, ring->dmabase);203203+ return -ENOMEM;204204+ }205205+ assert(!(ring->dmabase & 0x000003FF));206206+ memset(ring->vbase, 0, BCM43xx_DMA_RINGMEMSIZE);207207+208208+ return 0;209209+}210210+211211+static void free_ringmemory(struct bcm43xx_dmaring *ring)212212+{213213+ struct device *dev = &(ring->bcm->pci_dev->dev);214214+215215+ dma_free_coherent(dev, BCM43xx_DMA_RINGMEMSIZE,216216+ ring->vbase, ring->dmabase);217217+}218218+219219+/* Reset the RX DMA channel */220220+int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm,221221+ u16 mmio_base)222222+{223223+ int i;224224+ u32 value;225225+226226+ bcm43xx_write32(bcm,227227+ mmio_base + BCM43xx_DMA_RX_CONTROL,228228+ 0x00000000);229229+ for (i = 0; i < 1000; i++) {230230+ value = bcm43xx_read32(bcm,231231+ mmio_base + BCM43xx_DMA_RX_STATUS);232232+ value &= BCM43xx_DMA_RXSTAT_STAT_MASK;233233+ if (value == BCM43xx_DMA_RXSTAT_STAT_DISABLED) {234234+ i = -1;235235+ break;236236+ }237237+ udelay(10);238238+ }239239+ if (i != -1) {240240+ printk(KERN_ERR PFX "Error: Wait on DMA RX status timed out.\n");241241+ return -ENODEV;242242+ }243243+244244+ return 0;245245+}246246+247247+/* Reset the RX DMA channel */248248+int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm,249249+ u16 mmio_base)250250+{251251+ int i;252252+ u32 value;253253+254254+ for (i = 0; i < 1000; i++) {255255+ value = bcm43xx_read32(bcm,256256+ mmio_base + BCM43xx_DMA_TX_STATUS);257257+ value &= BCM43xx_DMA_TXSTAT_STAT_MASK;258258+ if (value == BCM43xx_DMA_TXSTAT_STAT_DISABLED ||259259+ value == BCM43xx_DMA_TXSTAT_STAT_IDLEWAIT ||260260+ value == BCM43xx_DMA_TXSTAT_STAT_STOPPED)261261+ break;262262+ udelay(10);263263+ }264264+ bcm43xx_write32(bcm,265265+ mmio_base + BCM43xx_DMA_TX_CONTROL,266266+ 0x00000000);267267+ for (i = 0; i < 1000; i++) {268268+ value = bcm43xx_read32(bcm,269269+ mmio_base + BCM43xx_DMA_TX_STATUS);270270+ value &= BCM43xx_DMA_TXSTAT_STAT_MASK;271271+ if (value == BCM43xx_DMA_TXSTAT_STAT_DISABLED) {272272+ i = -1;273273+ break;274274+ }275275+ udelay(10);276276+ }277277+ if (i != -1) {278278+ printk(KERN_ERR PFX "Error: Wait on DMA TX status timed out.\n");279279+ return -ENODEV;280280+ }281281+ /* ensure the reset is completed. */282282+ udelay(300);283283+284284+ return 0;285285+}286286+287287+static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring,288288+ struct bcm43xx_dmadesc *desc,289289+ struct bcm43xx_dmadesc_meta *meta,290290+ gfp_t gfp_flags)291291+{292292+ struct bcm43xx_rxhdr *rxhdr;293293+ dma_addr_t dmaaddr;294294+ u32 desc_addr;295295+ u32 desc_ctl;296296+ const int slot = (int)(desc - ring->vbase);297297+ struct sk_buff *skb;298298+299299+ assert(slot >= 0 && slot < ring->nr_slots);300300+ assert(!ring->tx);301301+302302+ skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags);303303+ if (unlikely(!skb))304304+ return -ENOMEM;305305+ dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0);306306+ if (unlikely(dmaaddr + ring->rx_buffersize > BCM43xx_DMA_BUSADDRMAX)) {307307+ unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0);308308+ dev_kfree_skb_any(skb);309309+ printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA RX SKB >1G "310310+ "(0x%08x, len: %u)\n",311311+ dmaaddr, ring->rx_buffersize);312312+ return -ENOMEM;313313+ }314314+ meta->skb = skb;315315+ meta->dmaaddr = dmaaddr;316316+ skb->dev = ring->bcm->net_dev;317317+ desc_addr = (u32)(dmaaddr + ring->memoffset);318318+ desc_ctl = (BCM43xx_DMADTOR_BYTECNT_MASK &319319+ (u32)(ring->rx_buffersize - ring->frameoffset));320320+ if (slot == ring->nr_slots - 1)321321+ desc_ctl |= BCM43xx_DMADTOR_DTABLEEND;322322+ set_desc_addr(desc, desc_addr);323323+ set_desc_ctl(desc, desc_ctl);324324+325325+ rxhdr = (struct bcm43xx_rxhdr *)(skb->data);326326+ rxhdr->frame_length = 0;327327+ rxhdr->flags1 = 0;328328+329329+ return 0;330330+}331331+332332+/* Allocate the initial descbuffers.333333+ * This is used for an RX ring only.334334+ */335335+static int alloc_initial_descbuffers(struct bcm43xx_dmaring *ring)336336+{337337+ int i, err = -ENOMEM;338338+ struct bcm43xx_dmadesc *desc;339339+ struct bcm43xx_dmadesc_meta *meta;340340+341341+ for (i = 0; i < ring->nr_slots; i++) {342342+ desc = ring->vbase + i;343343+ meta = ring->meta + i;344344+345345+ err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL);346346+ if (err)347347+ goto err_unwind;348348+ }349349+ ring->used_slots = ring->nr_slots;350350+ err = 0;351351+out:352352+ return err;353353+354354+err_unwind:355355+ for (i--; i >= 0; i--) {356356+ desc = ring->vbase + i;357357+ meta = ring->meta + i;358358+359359+ unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0);360360+ dev_kfree_skb(meta->skb);361361+ }362362+ goto out;363363+}364364+365365+/* Do initial setup of the DMA controller.366366+ * Reset the controller, write the ring busaddress367367+ * and switch the "enable" bit on.368368+ */369369+static int dmacontroller_setup(struct bcm43xx_dmaring *ring)370370+{371371+ int err = 0;372372+ u32 value;373373+374374+ if (ring->tx) {375375+ /* Set Transmit Control register to "transmit enable" */376376+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL,377377+ BCM43xx_DMA_TXCTRL_ENABLE);378378+ /* Set Transmit Descriptor ring address. */379379+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_RING,380380+ ring->dmabase + ring->memoffset);381381+ } else {382382+ err = alloc_initial_descbuffers(ring);383383+ if (err)384384+ goto out;385385+ /* Set Receive Control "receive enable" and frame offset */386386+ value = (ring->frameoffset << BCM43xx_DMA_RXCTRL_FRAMEOFF_SHIFT);387387+ value |= BCM43xx_DMA_RXCTRL_ENABLE;388388+ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_CONTROL, value);389389+ /* Set Receive Descriptor ring address. */390390+ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_RING,391391+ ring->dmabase + ring->memoffset);392392+ /* Init the descriptor pointer. */393393+ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_INDEX, 200);394394+ }395395+396396+out:397397+ return err;398398+}399399+400400+/* Shutdown the DMA controller. */401401+static void dmacontroller_cleanup(struct bcm43xx_dmaring *ring)402402+{403403+ if (ring->tx) {404404+ bcm43xx_dmacontroller_tx_reset(ring->bcm, ring->mmio_base);405405+ /* Zero out Transmit Descriptor ring address. */406406+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_RING, 0);407407+ } else {408408+ bcm43xx_dmacontroller_rx_reset(ring->bcm, ring->mmio_base);409409+ /* Zero out Receive Descriptor ring address. */410410+ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_RING, 0);411411+ }412412+}413413+414414+static void free_all_descbuffers(struct bcm43xx_dmaring *ring)415415+{416416+ struct bcm43xx_dmadesc *desc;417417+ struct bcm43xx_dmadesc_meta *meta;418418+ int i;419419+420420+ if (!ring->used_slots)421421+ return;422422+ for (i = 0; i < ring->nr_slots; i++) {423423+ desc = ring->vbase + i;424424+ meta = ring->meta + i;425425+426426+ if (!meta->skb) {427427+ assert(ring->tx);428428+ continue;429429+ }430430+ if (ring->tx) {431431+ unmap_descbuffer(ring, meta->dmaaddr,432432+ meta->skb->len, 1);433433+ } else {434434+ unmap_descbuffer(ring, meta->dmaaddr,435435+ ring->rx_buffersize, 0);436436+ }437437+ free_descriptor_buffer(ring, desc, meta, 0);438438+ }439439+}440440+441441+/* Main initialization function. */442442+static443443+struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm,444444+ u16 dma_controller_base,445445+ int nr_descriptor_slots,446446+ int tx)447447+{448448+ struct bcm43xx_dmaring *ring;449449+ int err;450450+451451+ ring = kzalloc(sizeof(*ring), GFP_KERNEL);452452+ if (!ring)453453+ goto out;454454+455455+ ring->meta = kzalloc(sizeof(*ring->meta) * nr_descriptor_slots,456456+ GFP_KERNEL);457457+ if (!ring->meta)458458+ goto err_kfree_ring;459459+460460+ ring->memoffset = BCM43xx_DMA_DMABUSADDROFFSET;461461+#ifdef CONFIG_BCM947XX462462+ if (bcm->pci_dev->bus->number == 0)463463+ ring->memoffset = 0;464464+#endif465465+466466+ ring->bcm = bcm;467467+ ring->nr_slots = nr_descriptor_slots;468468+ ring->suspend_mark = ring->nr_slots * BCM43xx_TXSUSPEND_PERCENT / 100;469469+ ring->resume_mark = ring->nr_slots * BCM43xx_TXRESUME_PERCENT / 100;470470+ assert(ring->suspend_mark < ring->resume_mark);471471+ ring->mmio_base = dma_controller_base;472472+ if (tx) {473473+ ring->tx = 1;474474+ ring->current_slot = -1;475475+ } else {476476+ switch (dma_controller_base) {477477+ case BCM43xx_MMIO_DMA1_BASE:478478+ ring->rx_buffersize = BCM43xx_DMA1_RXBUFFERSIZE;479479+ ring->frameoffset = BCM43xx_DMA1_RX_FRAMEOFFSET;480480+ break;481481+ case BCM43xx_MMIO_DMA4_BASE:482482+ ring->rx_buffersize = BCM43xx_DMA4_RXBUFFERSIZE;483483+ ring->frameoffset = BCM43xx_DMA4_RX_FRAMEOFFSET;484484+ break;485485+ default:486486+ assert(0);487487+ }488488+ }489489+490490+ err = alloc_ringmemory(ring);491491+ if (err)492492+ goto err_kfree_meta;493493+ err = dmacontroller_setup(ring);494494+ if (err)495495+ goto err_free_ringmemory;496496+497497+out:498498+ return ring;499499+500500+err_free_ringmemory:501501+ free_ringmemory(ring);502502+err_kfree_meta:503503+ kfree(ring->meta);504504+err_kfree_ring:505505+ kfree(ring);506506+ ring = NULL;507507+ goto out;508508+}509509+510510+/* Main cleanup function. */511511+static void bcm43xx_destroy_dmaring(struct bcm43xx_dmaring *ring)512512+{513513+ if (!ring)514514+ return;515515+516516+ dprintk(KERN_INFO PFX "DMA 0x%04x (%s) max used slots: %d/%d\n",517517+ ring->mmio_base,518518+ (ring->tx) ? "TX" : "RX",519519+ ring->max_used_slots, ring->nr_slots);520520+ /* Device IRQs are disabled prior entering this function,521521+ * so no need to take care of concurrency with rx handler stuff.522522+ */523523+ dmacontroller_cleanup(ring);524524+ free_all_descbuffers(ring);525525+ free_ringmemory(ring);526526+527527+ kfree(ring->meta);528528+ kfree(ring);529529+}530530+531531+void bcm43xx_dma_free(struct bcm43xx_private *bcm)532532+{533533+ struct bcm43xx_dma *dma;534534+535535+ if (bcm43xx_using_pio(bcm))536536+ return;537537+ dma = bcm43xx_current_dma(bcm);538538+539539+ bcm43xx_destroy_dmaring(dma->rx_ring1);540540+ dma->rx_ring1 = NULL;541541+ bcm43xx_destroy_dmaring(dma->rx_ring0);542542+ dma->rx_ring0 = NULL;543543+ bcm43xx_destroy_dmaring(dma->tx_ring3);544544+ dma->tx_ring3 = NULL;545545+ bcm43xx_destroy_dmaring(dma->tx_ring2);546546+ dma->tx_ring2 = NULL;547547+ bcm43xx_destroy_dmaring(dma->tx_ring1);548548+ dma->tx_ring1 = NULL;549549+ bcm43xx_destroy_dmaring(dma->tx_ring0);550550+ dma->tx_ring0 = NULL;551551+}552552+553553+int bcm43xx_dma_init(struct bcm43xx_private *bcm)554554+{555555+ struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm);556556+ struct bcm43xx_dmaring *ring;557557+ int err = -ENOMEM;558558+559559+ /* setup TX DMA channels. */560560+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA1_BASE,561561+ BCM43xx_TXRING_SLOTS, 1);562562+ if (!ring)563563+ goto out;564564+ dma->tx_ring0 = ring;565565+566566+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA2_BASE,567567+ BCM43xx_TXRING_SLOTS, 1);568568+ if (!ring)569569+ goto err_destroy_tx0;570570+ dma->tx_ring1 = ring;571571+572572+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA3_BASE,573573+ BCM43xx_TXRING_SLOTS, 1);574574+ if (!ring)575575+ goto err_destroy_tx1;576576+ dma->tx_ring2 = ring;577577+578578+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA4_BASE,579579+ BCM43xx_TXRING_SLOTS, 1);580580+ if (!ring)581581+ goto err_destroy_tx2;582582+ dma->tx_ring3 = ring;583583+584584+ /* setup RX DMA channels. */585585+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA1_BASE,586586+ BCM43xx_RXRING_SLOTS, 0);587587+ if (!ring)588588+ goto err_destroy_tx3;589589+ dma->rx_ring0 = ring;590590+591591+ if (bcm->current_core->rev < 5) {592592+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA4_BASE,593593+ BCM43xx_RXRING_SLOTS, 0);594594+ if (!ring)595595+ goto err_destroy_rx0;596596+ dma->rx_ring1 = ring;597597+ }598598+599599+ dprintk(KERN_INFO PFX "DMA initialized\n");600600+ err = 0;601601+out:602602+ return err;603603+604604+err_destroy_rx0:605605+ bcm43xx_destroy_dmaring(dma->rx_ring0);606606+ dma->rx_ring0 = NULL;607607+err_destroy_tx3:608608+ bcm43xx_destroy_dmaring(dma->tx_ring3);609609+ dma->tx_ring3 = NULL;610610+err_destroy_tx2:611611+ bcm43xx_destroy_dmaring(dma->tx_ring2);612612+ dma->tx_ring2 = NULL;613613+err_destroy_tx1:614614+ bcm43xx_destroy_dmaring(dma->tx_ring1);615615+ dma->tx_ring1 = NULL;616616+err_destroy_tx0:617617+ bcm43xx_destroy_dmaring(dma->tx_ring0);618618+ dma->tx_ring0 = NULL;619619+ goto out;620620+}621621+622622+/* Generate a cookie for the TX header. */623623+static u16 generate_cookie(struct bcm43xx_dmaring *ring,624624+ int slot)625625+{626626+ u16 cookie = 0x0000;627627+628628+ /* Use the upper 4 bits of the cookie as629629+ * DMA controller ID and store the slot number630630+ * in the lower 12 bits631631+ */632632+ switch (ring->mmio_base) {633633+ default:634634+ assert(0);635635+ case BCM43xx_MMIO_DMA1_BASE:636636+ break;637637+ case BCM43xx_MMIO_DMA2_BASE:638638+ cookie = 0x1000;639639+ break;640640+ case BCM43xx_MMIO_DMA3_BASE:641641+ cookie = 0x2000;642642+ break;643643+ case BCM43xx_MMIO_DMA4_BASE:644644+ cookie = 0x3000;645645+ break;646646+ }647647+ assert(((u16)slot & 0xF000) == 0x0000);648648+ cookie |= (u16)slot;649649+650650+ return cookie;651651+}652652+653653+/* Inspect a cookie and find out to which controller/slot it belongs. */654654+static655655+struct bcm43xx_dmaring * parse_cookie(struct bcm43xx_private *bcm,656656+ u16 cookie, int *slot)657657+{658658+ struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm);659659+ struct bcm43xx_dmaring *ring = NULL;660660+661661+ switch (cookie & 0xF000) {662662+ case 0x0000:663663+ ring = dma->tx_ring0;664664+ break;665665+ case 0x1000:666666+ ring = dma->tx_ring1;667667+ break;668668+ case 0x2000:669669+ ring = dma->tx_ring2;670670+ break;671671+ case 0x3000:672672+ ring = dma->tx_ring3;673673+ break;674674+ default:675675+ assert(0);676676+ }677677+ *slot = (cookie & 0x0FFF);678678+ assert(*slot >= 0 && *slot < ring->nr_slots);679679+680680+ return ring;681681+}682682+683683+static void dmacontroller_poke_tx(struct bcm43xx_dmaring *ring,684684+ int slot)685685+{686686+ /* Everything is ready to start. Buffers are DMA mapped and687687+ * associated with slots.688688+ * "slot" is the last slot of the new frame we want to transmit.689689+ * Close your seat belts now, please.690690+ */691691+ wmb();692692+ slot = next_slot(ring, slot);693693+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_INDEX,694694+ (u32)(slot * sizeof(struct bcm43xx_dmadesc)));695695+}696696+697697+static int dma_tx_fragment(struct bcm43xx_dmaring *ring,698698+ struct sk_buff *skb,699699+ u8 cur_frag)700700+{701701+ int slot;702702+ struct bcm43xx_dmadesc *desc;703703+ struct bcm43xx_dmadesc_meta *meta;704704+ u32 desc_ctl;705705+ u32 desc_addr;706706+707707+ assert(skb_shinfo(skb)->nr_frags == 0);708708+709709+ slot = request_slot(ring);710710+ desc = ring->vbase + slot;711711+ meta = ring->meta + slot;712712+713713+ /* Add a device specific TX header. */714714+ assert(skb_headroom(skb) >= sizeof(struct bcm43xx_txhdr));715715+ /* Reserve enough headroom for the device tx header. */716716+ __skb_push(skb, sizeof(struct bcm43xx_txhdr));717717+ /* Now calculate and add the tx header.718718+ * The tx header includes the PLCP header.719719+ */720720+ bcm43xx_generate_txhdr(ring->bcm,721721+ (struct bcm43xx_txhdr *)skb->data,722722+ skb->data + sizeof(struct bcm43xx_txhdr),723723+ skb->len - sizeof(struct bcm43xx_txhdr),724724+ (cur_frag == 0),725725+ generate_cookie(ring, slot));726726+727727+ meta->skb = skb;728728+ meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);729729+ if (unlikely(meta->dmaaddr + skb->len > BCM43xx_DMA_BUSADDRMAX)) {730730+ return_slot(ring, slot);731731+ printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA TX SKB >1G "732732+ "(0x%08x, len: %u)\n",733733+ meta->dmaaddr, skb->len);734734+ return -ENOMEM;735735+ }736736+737737+ desc_addr = (u32)(meta->dmaaddr + ring->memoffset);738738+ desc_ctl = BCM43xx_DMADTOR_FRAMESTART | BCM43xx_DMADTOR_FRAMEEND;739739+ desc_ctl |= BCM43xx_DMADTOR_COMPIRQ;740740+ desc_ctl |= (BCM43xx_DMADTOR_BYTECNT_MASK &741741+ (u32)(meta->skb->len - ring->frameoffset));742742+ if (slot == ring->nr_slots - 1)743743+ desc_ctl |= BCM43xx_DMADTOR_DTABLEEND;744744+745745+ set_desc_ctl(desc, desc_ctl);746746+ set_desc_addr(desc, desc_addr);747747+ /* Now transfer the whole frame. */748748+ dmacontroller_poke_tx(ring, slot);749749+750750+ return 0;751751+}752752+753753+int bcm43xx_dma_tx(struct bcm43xx_private *bcm,754754+ struct ieee80211_txb *txb)755755+{756756+ /* We just received a packet from the kernel network subsystem.757757+ * Add headers and DMA map the memory. Poke758758+ * the device to send the stuff.759759+ * Note that this is called from atomic context.760760+ */761761+ struct bcm43xx_dmaring *ring = bcm43xx_current_dma(bcm)->tx_ring1;762762+ u8 i;763763+ struct sk_buff *skb;764764+765765+ assert(ring->tx);766766+ if (unlikely(free_slots(ring) < txb->nr_frags)) {767767+ /* The queue should be stopped,768768+ * if we are low on free slots.769769+ * If this ever triggers, we have to lower the suspend_mark.770770+ */771771+ dprintkl(KERN_ERR PFX "Out of DMA descriptor slots!\n");772772+ return -ENOMEM;773773+ }774774+775775+ for (i = 0; i < txb->nr_frags; i++) {776776+ skb = txb->fragments[i];777777+ /* Take skb from ieee80211_txb_free */778778+ txb->fragments[i] = NULL;779779+ dma_tx_fragment(ring, skb, i);780780+ //TODO: handle failure of dma_tx_fragment781781+ }782782+ ieee80211_txb_free(txb);783783+784784+ return 0;785785+}786786+787787+void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm,788788+ struct bcm43xx_xmitstatus *status)789789+{790790+ struct bcm43xx_dmaring *ring;791791+ struct bcm43xx_dmadesc *desc;792792+ struct bcm43xx_dmadesc_meta *meta;793793+ int is_last_fragment;794794+ int slot;795795+796796+ ring = parse_cookie(bcm, status->cookie, &slot);797797+ assert(ring);798798+ assert(ring->tx);799799+ assert(get_desc_ctl(ring->vbase + slot) & BCM43xx_DMADTOR_FRAMESTART);800800+ while (1) {801801+ assert(slot >= 0 && slot < ring->nr_slots);802802+ desc = ring->vbase + slot;803803+ meta = ring->meta + slot;804804+805805+ is_last_fragment = !!(get_desc_ctl(desc) & BCM43xx_DMADTOR_FRAMEEND);806806+ unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1);807807+ free_descriptor_buffer(ring, desc, meta, 1);808808+ /* Everything belonging to the slot is unmapped809809+ * and freed, so we can return it.810810+ */811811+ return_slot(ring, slot);812812+813813+ if (is_last_fragment)814814+ break;815815+ slot = next_slot(ring, slot);816816+ }817817+ bcm->stats.last_tx = jiffies;818818+}819819+820820+static void dma_rx(struct bcm43xx_dmaring *ring,821821+ int *slot)822822+{823823+ struct bcm43xx_dmadesc *desc;824824+ struct bcm43xx_dmadesc_meta *meta;825825+ struct bcm43xx_rxhdr *rxhdr;826826+ struct sk_buff *skb;827827+ u16 len;828828+ int err;829829+ dma_addr_t dmaaddr;830830+831831+ desc = ring->vbase + *slot;832832+ meta = ring->meta + *slot;833833+834834+ sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize);835835+ skb = meta->skb;836836+837837+ if (ring->mmio_base == BCM43xx_MMIO_DMA4_BASE) {838838+ /* We received an xmit status. */839839+ struct bcm43xx_hwxmitstatus *hw = (struct bcm43xx_hwxmitstatus *)skb->data;840840+ struct bcm43xx_xmitstatus stat;841841+842842+ stat.cookie = le16_to_cpu(hw->cookie);843843+ stat.flags = hw->flags;844844+ stat.cnt1 = hw->cnt1;845845+ stat.cnt2 = hw->cnt2;846846+ stat.seq = le16_to_cpu(hw->seq);847847+ stat.unknown = le16_to_cpu(hw->unknown);848848+849849+ bcm43xx_debugfs_log_txstat(ring->bcm, &stat);850850+ bcm43xx_dma_handle_xmitstatus(ring->bcm, &stat);851851+ /* recycle the descriptor buffer. */852852+ sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize);853853+854854+ return;855855+ }856856+ rxhdr = (struct bcm43xx_rxhdr *)skb->data;857857+ len = le16_to_cpu(rxhdr->frame_length);858858+ if (len == 0) {859859+ int i = 0;860860+861861+ do {862862+ udelay(2);863863+ barrier();864864+ len = le16_to_cpu(rxhdr->frame_length);865865+ } while (len == 0 && i++ < 5);866866+ if (unlikely(len == 0)) {867867+ /* recycle the descriptor buffer. */868868+ sync_descbuffer_for_device(ring, meta->dmaaddr,869869+ ring->rx_buffersize);870870+ goto drop;871871+ }872872+ }873873+ if (unlikely(len > ring->rx_buffersize)) {874874+ /* The data did not fit into one descriptor buffer875875+ * and is split over multiple buffers.876876+ * This should never happen, as we try to allocate buffers877877+ * big enough. So simply ignore this packet.878878+ */879879+ int cnt = 0;880880+ s32 tmp = len;881881+882882+ while (1) {883883+ desc = ring->vbase + *slot;884884+ meta = ring->meta + *slot;885885+ /* recycle the descriptor buffer. */886886+ sync_descbuffer_for_device(ring, meta->dmaaddr,887887+ ring->rx_buffersize);888888+ *slot = next_slot(ring, *slot);889889+ cnt++;890890+ tmp -= ring->rx_buffersize;891891+ if (tmp <= 0)892892+ break;893893+ }894894+ printkl(KERN_ERR PFX "DMA RX buffer too small "895895+ "(len: %u, buffer: %u, nr-dropped: %d)\n",896896+ len, ring->rx_buffersize, cnt);897897+ goto drop;898898+ }899899+ len -= IEEE80211_FCS_LEN;900900+901901+ dmaaddr = meta->dmaaddr;902902+ err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC);903903+ if (unlikely(err)) {904904+ dprintkl(KERN_ERR PFX "DMA RX: setup_rx_descbuffer() failed\n");905905+ sync_descbuffer_for_device(ring, dmaaddr,906906+ ring->rx_buffersize);907907+ goto drop;908908+ }909909+910910+ unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0);911911+ skb_put(skb, len + ring->frameoffset);912912+ skb_pull(skb, ring->frameoffset);913913+914914+ err = bcm43xx_rx(ring->bcm, skb, rxhdr);915915+ if (err) {916916+ dev_kfree_skb_irq(skb);917917+ goto drop;918918+ }919919+920920+drop:921921+ return;922922+}923923+924924+void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring)925925+{926926+ u32 status;927927+ u16 descptr;928928+ int slot, current_slot;929929+#ifdef CONFIG_BCM43XX_DEBUG930930+ int used_slots = 0;931931+#endif932932+933933+ assert(!ring->tx);934934+ status = bcm43xx_dma_read(ring, BCM43xx_DMA_RX_STATUS);935935+ descptr = (status & BCM43xx_DMA_RXSTAT_DPTR_MASK);936936+ current_slot = descptr / sizeof(struct bcm43xx_dmadesc);937937+ assert(current_slot >= 0 && current_slot < ring->nr_slots);938938+939939+ slot = ring->current_slot;940940+ for ( ; slot != current_slot; slot = next_slot(ring, slot)) {941941+ dma_rx(ring, &slot);942942+#ifdef CONFIG_BCM43XX_DEBUG943943+ if (++used_slots > ring->max_used_slots)944944+ ring->max_used_slots = used_slots;945945+#endif946946+ }947947+ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_INDEX,948948+ (u32)(slot * sizeof(struct bcm43xx_dmadesc)));949949+ ring->current_slot = slot;950950+}951951+952952+void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring)953953+{954954+ assert(ring->tx);955955+ bcm43xx_power_saving_ctl_bits(ring->bcm, -1, 1);956956+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL,957957+ bcm43xx_dma_read(ring, BCM43xx_DMA_TX_CONTROL)958958+ | BCM43xx_DMA_TXCTRL_SUSPEND);959959+}960960+961961+void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring)962962+{963963+ assert(ring->tx);964964+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL,965965+ bcm43xx_dma_read(ring, BCM43xx_DMA_TX_CONTROL)966966+ & ~BCM43xx_DMA_TXCTRL_SUSPEND);967967+ bcm43xx_power_saving_ctl_bits(ring->bcm, -1, -1);968968+}
+218
drivers/net/wireless/bcm43xx/bcm43xx_dma.h
···11+#ifndef BCM43xx_DMA_H_22+#define BCM43xx_DMA_H_33+44+#include <linux/list.h>55+#include <linux/spinlock.h>66+#include <linux/workqueue.h>77+#include <linux/linkage.h>88+#include <asm/atomic.h>99+1010+1111+/* DMA-Interrupt reasons. */1212+#define BCM43xx_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \1313+ | (1 << 14) | (1 << 15))1414+#define BCM43xx_DMAIRQ_NONFATALMASK (1 << 13)1515+#define BCM43xx_DMAIRQ_RX_DONE (1 << 16)1616+1717+/* DMA controller register offsets. (relative to BCM43xx_DMA#_BASE) */1818+#define BCM43xx_DMA_TX_CONTROL 0x001919+#define BCM43xx_DMA_TX_DESC_RING 0x042020+#define BCM43xx_DMA_TX_DESC_INDEX 0x082121+#define BCM43xx_DMA_TX_STATUS 0x0c2222+#define BCM43xx_DMA_RX_CONTROL 0x102323+#define BCM43xx_DMA_RX_DESC_RING 0x142424+#define BCM43xx_DMA_RX_DESC_INDEX 0x182525+#define BCM43xx_DMA_RX_STATUS 0x1c2626+2727+/* DMA controller channel control word values. */2828+#define BCM43xx_DMA_TXCTRL_ENABLE (1 << 0)2929+#define BCM43xx_DMA_TXCTRL_SUSPEND (1 << 1)3030+#define BCM43xx_DMA_TXCTRL_LOOPBACK (1 << 2)3131+#define BCM43xx_DMA_TXCTRL_FLUSH (1 << 4)3232+#define BCM43xx_DMA_RXCTRL_ENABLE (1 << 0)3333+#define BCM43xx_DMA_RXCTRL_FRAMEOFF_MASK 0x000000fe3434+#define BCM43xx_DMA_RXCTRL_FRAMEOFF_SHIFT 13535+#define BCM43xx_DMA_RXCTRL_PIO (1 << 8)3636+/* DMA controller channel status word values. */3737+#define BCM43xx_DMA_TXSTAT_DPTR_MASK 0x00000fff3838+#define BCM43xx_DMA_TXSTAT_STAT_MASK 0x0000f0003939+#define BCM43xx_DMA_TXSTAT_STAT_DISABLED 0x000000004040+#define BCM43xx_DMA_TXSTAT_STAT_ACTIVE 0x000010004141+#define BCM43xx_DMA_TXSTAT_STAT_IDLEWAIT 0x000020004242+#define BCM43xx_DMA_TXSTAT_STAT_STOPPED 0x000030004343+#define BCM43xx_DMA_TXSTAT_STAT_SUSP 0x000040004444+#define BCM43xx_DMA_TXSTAT_ERROR_MASK 0x000f00004545+#define BCM43xx_DMA_TXSTAT_FLUSHED (1 << 20)4646+#define BCM43xx_DMA_RXSTAT_DPTR_MASK 0x00000fff4747+#define BCM43xx_DMA_RXSTAT_STAT_MASK 0x0000f0004848+#define BCM43xx_DMA_RXSTAT_STAT_DISABLED 0x000000004949+#define BCM43xx_DMA_RXSTAT_STAT_ACTIVE 0x000010005050+#define BCM43xx_DMA_RXSTAT_STAT_IDLEWAIT 0x000020005151+#define BCM43xx_DMA_RXSTAT_STAT_RESERVED 0x000030005252+#define BCM43xx_DMA_RXSTAT_STAT_ERRORS 0x000040005353+#define BCM43xx_DMA_RXSTAT_ERROR_MASK 0x000f00005454+5555+/* DMA descriptor control field values. */5656+#define BCM43xx_DMADTOR_BYTECNT_MASK 0x00001fff5757+#define BCM43xx_DMADTOR_DTABLEEND (1 << 28) /* End of descriptor table */5858+#define BCM43xx_DMADTOR_COMPIRQ (1 << 29) /* IRQ on completion request */5959+#define BCM43xx_DMADTOR_FRAMEEND (1 << 30)6060+#define BCM43xx_DMADTOR_FRAMESTART (1 << 31)6161+6262+/* Misc DMA constants */6363+#define BCM43xx_DMA_RINGMEMSIZE PAGE_SIZE6464+#define BCM43xx_DMA_BUSADDRMAX 0x3FFFFFFF6565+#define BCM43xx_DMA_DMABUSADDROFFSET (1 << 30)6666+#define BCM43xx_DMA1_RX_FRAMEOFFSET 306767+#define BCM43xx_DMA4_RX_FRAMEOFFSET 06868+6969+/* DMA engine tuning knobs */7070+#define BCM43xx_TXRING_SLOTS 5127171+#define BCM43xx_RXRING_SLOTS 647272+#define BCM43xx_DMA1_RXBUFFERSIZE (2304 + 100)7373+#define BCM43xx_DMA4_RXBUFFERSIZE 167474+/* Suspend the tx queue, if less than this percent slots are free. */7575+#define BCM43xx_TXSUSPEND_PERCENT 207676+/* Resume the tx queue, if more than this percent slots are free. */7777+#define BCM43xx_TXRESUME_PERCENT 507878+7979+8080+8181+#ifdef CONFIG_BCM43XX_DMA8282+8383+8484+struct sk_buff;8585+struct bcm43xx_private;8686+struct bcm43xx_xmitstatus;8787+8888+8989+struct bcm43xx_dmadesc {9090+ __le32 _control;9191+ __le32 _address;9292+} __attribute__((__packed__));9393+9494+/* Macros to access the bcm43xx_dmadesc struct */9595+#define get_desc_ctl(desc) le32_to_cpu((desc)->_control)9696+#define set_desc_ctl(desc, ctl) do { (desc)->_control = cpu_to_le32(ctl); } while (0)9797+#define get_desc_addr(desc) le32_to_cpu((desc)->_address)9898+#define set_desc_addr(desc, addr) do { (desc)->_address = cpu_to_le32(addr); } while (0)9999+100100+struct bcm43xx_dmadesc_meta {101101+ /* The kernel DMA-able buffer. */102102+ struct sk_buff *skb;103103+ /* DMA base bus-address of the descriptor buffer. */104104+ dma_addr_t dmaaddr;105105+};106106+107107+struct bcm43xx_dmaring {108108+ struct bcm43xx_private *bcm;109109+ /* Kernel virtual base address of the ring memory. */110110+ struct bcm43xx_dmadesc *vbase;111111+ /* DMA memory offset */112112+ dma_addr_t memoffset;113113+ /* (Unadjusted) DMA base bus-address of the ring memory. */114114+ dma_addr_t dmabase;115115+ /* Meta data about all descriptors. */116116+ struct bcm43xx_dmadesc_meta *meta;117117+ /* Number of descriptor slots in the ring. */118118+ int nr_slots;119119+ /* Number of used descriptor slots. */120120+ int used_slots;121121+ /* Currently used slot in the ring. */122122+ int current_slot;123123+ /* Marks to suspend/resume the queue. */124124+ int suspend_mark;125125+ int resume_mark;126126+ /* Frameoffset in octets. */127127+ u32 frameoffset;128128+ /* Descriptor buffer size. */129129+ u16 rx_buffersize;130130+ /* The MMIO base register of the DMA controller, this131131+ * ring is posted to.132132+ */133133+ u16 mmio_base;134134+ u8 tx:1, /* TRUE, if this is a TX ring. */135135+ suspended:1; /* TRUE, if transfers are suspended on this ring. */136136+#ifdef CONFIG_BCM43XX_DEBUG137137+ /* Maximum number of used slots. */138138+ int max_used_slots;139139+#endif /* CONFIG_BCM43XX_DEBUG*/140140+};141141+142142+143143+static inline144144+u32 bcm43xx_dma_read(struct bcm43xx_dmaring *ring,145145+ u16 offset)146146+{147147+ return bcm43xx_read32(ring->bcm, ring->mmio_base + offset);148148+}149149+150150+static inline151151+void bcm43xx_dma_write(struct bcm43xx_dmaring *ring,152152+ u16 offset, u32 value)153153+{154154+ bcm43xx_write32(ring->bcm, ring->mmio_base + offset, value);155155+}156156+157157+158158+int bcm43xx_dma_init(struct bcm43xx_private *bcm);159159+void bcm43xx_dma_free(struct bcm43xx_private *bcm);160160+161161+int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm,162162+ u16 dmacontroller_mmio_base);163163+int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm,164164+ u16 dmacontroller_mmio_base);165165+166166+void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring);167167+void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring);168168+169169+void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm,170170+ struct bcm43xx_xmitstatus *status);171171+172172+int bcm43xx_dma_tx(struct bcm43xx_private *bcm,173173+ struct ieee80211_txb *txb);174174+void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring);175175+176176+177177+#else /* CONFIG_BCM43XX_DMA */178178+179179+180180+static inline181181+int bcm43xx_dma_init(struct bcm43xx_private *bcm)182182+{183183+ return 0;184184+}185185+static inline186186+void bcm43xx_dma_free(struct bcm43xx_private *bcm)187187+{188188+}189189+static inline190190+int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm,191191+ u16 dmacontroller_mmio_base)192192+{193193+ return 0;194194+}195195+static inline196196+int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm,197197+ u16 dmacontroller_mmio_base)198198+{199199+ return 0;200200+}201201+static inline202202+int bcm43xx_dma_tx(struct bcm43xx_private *bcm,203203+ struct ieee80211_txb *txb)204204+{205205+ return 0;206206+}207207+static inline208208+void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm,209209+ struct bcm43xx_xmitstatus *status)210210+{211211+}212212+static inline213213+void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring)214214+{215215+}216216+217217+#endif /* CONFIG_BCM43XX_DMA */218218+#endif /* BCM43xx_DMA_H_ */
+50
drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ ethtool support66+77+ Copyright (c) 2006 Jason Lunz <lunz@falooley.org>88+99+ Some code in this file is derived from the 8139too.c driver1010+ Copyright (C) 2002 Jeff Garzik1111+1212+ This program is free software; you can redistribute it and/or modify1313+ it under the terms of the GNU General Public License as published by1414+ the Free Software Foundation; either version 2 of the License, or1515+ (at your option) any later version.1616+1717+ This program is distributed in the hope that it will be useful,1818+ but WITHOUT ANY WARRANTY; without even the implied warranty of1919+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the2020+ GNU General Public License for more details.2121+2222+ You should have received a copy of the GNU General Public License2323+ along with this program; see the file COPYING. If not, write to2424+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2525+ Boston, MA 02110-1301, USA.2626+2727+*/2828+2929+#include "bcm43xx.h"3030+#include "bcm43xx_ethtool.h"3131+3232+#include <linux/netdevice.h>3333+#include <linux/pci.h>3434+#include <linux/string.h>3535+#include <linux/version.h>3636+3737+3838+static void bcm43xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)3939+{4040+ struct bcm43xx_private *bcm = bcm43xx_priv(dev);4141+4242+ strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));4343+ strncpy(info->version, UTS_RELEASE, sizeof(info->version));4444+ strncpy(info->bus_info, pci_name(bcm->pci_dev), ETHTOOL_BUSINFO_LEN);4545+}4646+4747+struct ethtool_ops bcm43xx_ethtool_ops = {4848+ .get_drvinfo = bcm43xx_get_drvinfo,4949+ .get_link = ethtool_op_get_link,5050+};
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,66+ Stefano Brivio <st3@riseup.net>77+ Michael Buesch <mbuesch@freenet.de>88+ Danny van Dyk <kugelfang@gentoo.org>99+ Andreas Jaggi <andreas.jaggi@waterwave.ch>1010+1111+ This program is free software; you can redistribute it and/or modify1212+ it under the terms of the GNU General Public License as published by1313+ the Free Software Foundation; either version 2 of the License, or1414+ (at your option) any later version.1515+1616+ This program is distributed in the hope that it will be useful,1717+ but WITHOUT ANY WARRANTY; without even the implied warranty of1818+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1919+ GNU General Public License for more details.2020+2121+ You should have received a copy of the GNU General Public License2222+ along with this program; see the file COPYING. If not, write to2323+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2424+ Boston, MA 02110-1301, USA.2525+2626+*/2727+2828+#include "bcm43xx_leds.h"2929+#include "bcm43xx.h"3030+3131+#include <asm/bitops.h>3232+3333+3434+static void bcm43xx_led_changestate(struct bcm43xx_led *led)3535+{3636+ struct bcm43xx_private *bcm = led->bcm;3737+ const int index = bcm43xx_led_index(led);3838+ const u16 mask = (1 << index);3939+ u16 ledctl;4040+4141+ assert(index >= 0 && index < BCM43xx_NR_LEDS);4242+ assert(led->blink_interval);4343+ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);4444+ ledctl = (ledctl & mask) ? (ledctl & ~mask) : (ledctl | mask);4545+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);4646+}4747+4848+static void bcm43xx_led_blink(unsigned long d)4949+{5050+ struct bcm43xx_led *led = (struct bcm43xx_led *)d;5151+ struct bcm43xx_private *bcm = led->bcm;5252+ unsigned long flags;5353+5454+ bcm43xx_lock_mmio(bcm, flags);5555+ if (led->blink_interval) {5656+ bcm43xx_led_changestate(led);5757+ mod_timer(&led->blink_timer, jiffies + led->blink_interval);5858+ }5959+ bcm43xx_unlock_mmio(bcm, flags);6060+}6161+6262+static void bcm43xx_led_blink_start(struct bcm43xx_led *led,6363+ unsigned long interval)6464+{6565+ if (led->blink_interval)6666+ return;6767+ led->blink_interval = interval;6868+ bcm43xx_led_changestate(led);6969+ led->blink_timer.expires = jiffies + interval;7070+ add_timer(&led->blink_timer);7171+}7272+7373+static void bcm43xx_led_blink_stop(struct bcm43xx_led *led, int sync)7474+{7575+ struct bcm43xx_private *bcm = led->bcm;7676+ const int index = bcm43xx_led_index(led);7777+ u16 ledctl;7878+7979+ if (!led->blink_interval)8080+ return;8181+ if (unlikely(sync))8282+ del_timer_sync(&led->blink_timer);8383+ else8484+ del_timer(&led->blink_timer);8585+ led->blink_interval = 0;8686+8787+ /* Make sure the LED is turned off. */8888+ assert(index >= 0 && index < BCM43xx_NR_LEDS);8989+ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);9090+ if (led->activelow)9191+ ledctl |= (1 << index);9292+ else9393+ ledctl &= ~(1 << index);9494+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);9595+}9696+9797+static void bcm43xx_led_init_hardcoded(struct bcm43xx_private *bcm,9898+ struct bcm43xx_led *led,9999+ int led_index)100100+{101101+ /* This function is called, if the behaviour (and activelow)102102+ * information for a LED is missing in the SPROM.103103+ * We hardcode the behaviour values for various devices here.104104+ * Note that the BCM43xx_LED_TEST_XXX behaviour values can105105+ * be used to figure out which led is mapped to which index.106106+ */107107+108108+ switch (led_index) {109109+ case 0:110110+ led->behaviour = BCM43xx_LED_ACTIVITY;111111+ if (bcm->board_vendor == PCI_VENDOR_ID_COMPAQ)112112+ led->behaviour = BCM43xx_LED_RADIO_ALL;113113+ break;114114+ case 1:115115+ led->behaviour = BCM43xx_LED_RADIO_B;116116+ if (bcm->board_vendor == PCI_VENDOR_ID_ASUSTEK)117117+ led->behaviour = BCM43xx_LED_ASSOC;118118+ break;119119+ case 2:120120+ led->behaviour = BCM43xx_LED_RADIO_A;121121+ break;122122+ case 3:123123+ led->behaviour = BCM43xx_LED_OFF;124124+ break;125125+ default:126126+ assert(0);127127+ }128128+}129129+130130+int bcm43xx_leds_init(struct bcm43xx_private *bcm)131131+{132132+ struct bcm43xx_led *led;133133+ u8 sprom[4];134134+ int i;135135+136136+ sprom[0] = bcm->sprom.wl0gpio0;137137+ sprom[1] = bcm->sprom.wl0gpio1;138138+ sprom[2] = bcm->sprom.wl0gpio2;139139+ sprom[3] = bcm->sprom.wl0gpio3;140140+141141+ for (i = 0; i < BCM43xx_NR_LEDS; i++) {142142+ led = &(bcm->leds[i]);143143+ led->bcm = bcm;144144+ setup_timer(&led->blink_timer,145145+ bcm43xx_led_blink,146146+ (unsigned long)led);147147+148148+ if (sprom[i] == 0xFF) {149149+ bcm43xx_led_init_hardcoded(bcm, led, i);150150+ } else {151151+ led->behaviour = sprom[i] & BCM43xx_LED_BEHAVIOUR;152152+ led->activelow = !!(sprom[i] & BCM43xx_LED_ACTIVELOW);153153+ }154154+ }155155+156156+ return 0;157157+}158158+159159+void bcm43xx_leds_exit(struct bcm43xx_private *bcm)160160+{161161+ struct bcm43xx_led *led;162162+ int i;163163+164164+ for (i = 0; i < BCM43xx_NR_LEDS; i++) {165165+ led = &(bcm->leds[i]);166166+ bcm43xx_led_blink_stop(led, 1);167167+ }168168+ bcm43xx_leds_switch_all(bcm, 0);169169+}170170+171171+void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity)172172+{173173+ struct bcm43xx_led *led;174174+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);175175+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);176176+ const int transferring = (jiffies - bcm->stats.last_tx) < BCM43xx_LED_XFER_THRES;177177+ int i, turn_on;178178+ unsigned long interval = 0;179179+ u16 ledctl;180180+181181+ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);182182+ for (i = 0; i < BCM43xx_NR_LEDS; i++) {183183+ led = &(bcm->leds[i]);184184+185185+ turn_on = 0;186186+ switch (led->behaviour) {187187+ case BCM43xx_LED_INACTIVE:188188+ continue;189189+ case BCM43xx_LED_OFF:190190+ break;191191+ case BCM43xx_LED_ON:192192+ turn_on = 1;193193+ break;194194+ case BCM43xx_LED_ACTIVITY:195195+ turn_on = activity;196196+ break;197197+ case BCM43xx_LED_RADIO_ALL:198198+ turn_on = radio->enabled;199199+ break;200200+ case BCM43xx_LED_RADIO_A:201201+ turn_on = (radio->enabled && phy->type == BCM43xx_PHYTYPE_A);202202+ break;203203+ case BCM43xx_LED_RADIO_B:204204+ turn_on = (radio->enabled &&205205+ (phy->type == BCM43xx_PHYTYPE_B ||206206+ phy->type == BCM43xx_PHYTYPE_G));207207+ break;208208+ case BCM43xx_LED_MODE_BG:209209+ if (phy->type == BCM43xx_PHYTYPE_G &&210210+ 1/*FIXME: using G rates.*/)211211+ turn_on = 1;212212+ break;213213+ case BCM43xx_LED_TRANSFER:214214+ if (transferring)215215+ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM);216216+ else217217+ bcm43xx_led_blink_stop(led, 0);218218+ continue;219219+ case BCM43xx_LED_APTRANSFER:220220+ if (bcm->ieee->iw_mode == IW_MODE_MASTER) {221221+ if (transferring) {222222+ interval = BCM43xx_LEDBLINK_FAST;223223+ turn_on = 1;224224+ }225225+ } else {226226+ turn_on = 1;227227+ if (0/*TODO: not assoc*/)228228+ interval = BCM43xx_LEDBLINK_SLOW;229229+ else if (transferring)230230+ interval = BCM43xx_LEDBLINK_FAST;231231+ else232232+ turn_on = 0;233233+ }234234+ if (turn_on)235235+ bcm43xx_led_blink_start(led, interval);236236+ else237237+ bcm43xx_led_blink_stop(led, 0);238238+ continue;239239+ case BCM43xx_LED_WEIRD:240240+ //TODO241241+ break;242242+ case BCM43xx_LED_ASSOC:243243+ if (bcm->softmac->associated)244244+ turn_on = 1;245245+ break;246246+#ifdef CONFIG_BCM43XX_DEBUG247247+ case BCM43xx_LED_TEST_BLINKSLOW:248248+ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_SLOW);249249+ continue;250250+ case BCM43xx_LED_TEST_BLINKMEDIUM:251251+ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM);252252+ continue;253253+ case BCM43xx_LED_TEST_BLINKFAST:254254+ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_FAST);255255+ continue;256256+#endif /* CONFIG_BCM43XX_DEBUG */257257+ default:258258+ assert(0);259259+ };260260+261261+ if (led->activelow)262262+ turn_on = !turn_on;263263+ if (turn_on)264264+ ledctl |= (1 << i);265265+ else266266+ ledctl &= ~(1 << i);267267+ }268268+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);269269+}270270+271271+void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on)272272+{273273+ struct bcm43xx_led *led;274274+ u16 ledctl;275275+ int i;276276+ int bit_on;277277+278278+ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);279279+ for (i = 0; i < BCM43xx_NR_LEDS; i++) {280280+ led = &(bcm->leds[i]);281281+ if (led->behaviour == BCM43xx_LED_INACTIVE)282282+ continue;283283+ if (on)284284+ bit_on = led->activelow ? 0 : 1;285285+ else286286+ bit_on = led->activelow ? 1 : 0;287287+ if (bit_on)288288+ ledctl |= (1 << i);289289+ else290290+ ledctl &= ~(1 << i);291291+ }292292+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);293293+}
+56
drivers/net/wireless/bcm43xx/bcm43xx_leds.h
···11+#ifndef BCM43xx_LEDS_H_22+#define BCM43xx_LEDS_H_33+44+#include <linux/types.h>55+#include <linux/timer.h>66+77+88+struct bcm43xx_led {99+ u8 behaviour:7;1010+ u8 activelow:1;1111+1212+ struct bcm43xx_private *bcm;1313+ struct timer_list blink_timer;1414+ unsigned long blink_interval;1515+};1616+#define bcm43xx_led_index(led) ((int)((led) - (led)->bcm->leds))1717+1818+/* Delay between state changes when blinking in jiffies */1919+#define BCM43xx_LEDBLINK_SLOW (HZ / 1)2020+#define BCM43xx_LEDBLINK_MEDIUM (HZ / 4)2121+#define BCM43xx_LEDBLINK_FAST (HZ / 8)2222+2323+#define BCM43xx_LED_XFER_THRES (HZ / 100)2424+2525+#define BCM43xx_LED_BEHAVIOUR 0x7F2626+#define BCM43xx_LED_ACTIVELOW 0x802727+enum { /* LED behaviour values */2828+ BCM43xx_LED_OFF,2929+ BCM43xx_LED_ON,3030+ BCM43xx_LED_ACTIVITY,3131+ BCM43xx_LED_RADIO_ALL,3232+ BCM43xx_LED_RADIO_A,3333+ BCM43xx_LED_RADIO_B,3434+ BCM43xx_LED_MODE_BG,3535+ BCM43xx_LED_TRANSFER,3636+ BCM43xx_LED_APTRANSFER,3737+ BCM43xx_LED_WEIRD,//FIXME3838+ BCM43xx_LED_ASSOC,3939+ BCM43xx_LED_INACTIVE,4040+4141+ /* Behaviour values for testing.4242+ * With these values it is easier to figure out4343+ * the real behaviour of leds, in case the SPROM4444+ * is missing information.4545+ */4646+ BCM43xx_LED_TEST_BLINKSLOW,4747+ BCM43xx_LED_TEST_BLINKMEDIUM,4848+ BCM43xx_LED_TEST_BLINKFAST,4949+};5050+5151+int bcm43xx_leds_init(struct bcm43xx_private *bcm);5252+void bcm43xx_leds_exit(struct bcm43xx_private *bcm);5353+void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity);5454+void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on);5555+5656+#endif /* BCM43xx_LEDS_H_ */
+3973
drivers/net/wireless/bcm43xx/bcm43xx_main.c
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,66+ Stefano Brivio <st3@riseup.net>77+ Michael Buesch <mbuesch@freenet.de>88+ Danny van Dyk <kugelfang@gentoo.org>99+ Andreas Jaggi <andreas.jaggi@waterwave.ch>1010+1111+ Some parts of the code in this file are derived from the ipw22001212+ driver Copyright(c) 2003 - 2004 Intel Corporation.1313+1414+ This program is free software; you can redistribute it and/or modify1515+ it under the terms of the GNU General Public License as published by1616+ the Free Software Foundation; either version 2 of the License, or1717+ (at your option) any later version.1818+1919+ This program is distributed in the hope that it will be useful,2020+ but WITHOUT ANY WARRANTY; without even the implied warranty of2121+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the2222+ GNU General Public License for more details.2323+2424+ You should have received a copy of the GNU General Public License2525+ along with this program; see the file COPYING. If not, write to2626+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2727+ Boston, MA 02110-1301, USA.2828+2929+*/3030+3131+#include <linux/delay.h>3232+#include <linux/init.h>3333+#include <linux/moduleparam.h>3434+#include <linux/if_arp.h>3535+#include <linux/etherdevice.h>3636+#include <linux/version.h>3737+#include <linux/firmware.h>3838+#include <linux/wireless.h>3939+#include <linux/workqueue.h>4040+#include <linux/skbuff.h>4141+#include <linux/dma-mapping.h>4242+#include <net/iw_handler.h>4343+4444+#include "bcm43xx.h"4545+#include "bcm43xx_main.h"4646+#include "bcm43xx_debugfs.h"4747+#include "bcm43xx_radio.h"4848+#include "bcm43xx_phy.h"4949+#include "bcm43xx_dma.h"5050+#include "bcm43xx_pio.h"5151+#include "bcm43xx_power.h"5252+#include "bcm43xx_wx.h"5353+#include "bcm43xx_ethtool.h"5454+#include "bcm43xx_xmit.h"5555+5656+5757+MODULE_DESCRIPTION("Broadcom BCM43xx wireless driver");5858+MODULE_AUTHOR("Martin Langer");5959+MODULE_AUTHOR("Stefano Brivio");6060+MODULE_AUTHOR("Michael Buesch");6161+MODULE_LICENSE("GPL");6262+6363+#ifdef CONFIG_BCM947XX6464+extern char *nvram_get(char *name);6565+#endif6666+6767+#if defined(CONFIG_BCM43XX_DMA) && defined(CONFIG_BCM43XX_PIO)6868+static int modparam_pio;6969+module_param_named(pio, modparam_pio, int, 0444);7070+MODULE_PARM_DESC(pio, "enable(1) / disable(0) PIO mode");7171+#elif defined(CONFIG_BCM43XX_DMA)7272+# define modparam_pio 07373+#elif defined(CONFIG_BCM43XX_PIO)7474+# define modparam_pio 17575+#endif7676+7777+static int modparam_bad_frames_preempt;7878+module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444);7979+MODULE_PARM_DESC(bad_frames_preempt, "enable(1) / disable(0) Bad Frames Preemption");8080+8181+static int modparam_short_retry = BCM43xx_DEFAULT_SHORT_RETRY_LIMIT;8282+module_param_named(short_retry, modparam_short_retry, int, 0444);8383+MODULE_PARM_DESC(short_retry, "Short-Retry-Limit (0 - 15)");8484+8585+static int modparam_long_retry = BCM43xx_DEFAULT_LONG_RETRY_LIMIT;8686+module_param_named(long_retry, modparam_long_retry, int, 0444);8787+MODULE_PARM_DESC(long_retry, "Long-Retry-Limit (0 - 15)");8888+8989+static int modparam_locale = -1;9090+module_param_named(locale, modparam_locale, int, 0444);9191+MODULE_PARM_DESC(country, "Select LocaleCode 0-11 (For travelers)");9292+9393+static int modparam_noleds;9494+module_param_named(noleds, modparam_noleds, int, 0444);9595+MODULE_PARM_DESC(noleds, "Turn off all LED activity");9696+9797+#ifdef CONFIG_BCM43XX_DEBUG9898+static char modparam_fwpostfix[64];9999+module_param_string(fwpostfix, modparam_fwpostfix, 64, 0444);100100+MODULE_PARM_DESC(fwpostfix, "Postfix for .fw files. Useful for debugging.");101101+#else102102+# define modparam_fwpostfix ""103103+#endif /* CONFIG_BCM43XX_DEBUG*/104104+105105+106106+/* If you want to debug with just a single device, enable this,107107+ * where the string is the pci device ID (as given by the kernel's108108+ * pci_name function) of the device to be used.109109+ */110110+//#define DEBUG_SINGLE_DEVICE_ONLY "0001:11:00.0"111111+112112+/* If you want to enable printing of each MMIO access, enable this. */113113+//#define DEBUG_ENABLE_MMIO_PRINT114114+115115+/* If you want to enable printing of MMIO access within116116+ * ucode/pcm upload, initvals write, enable this.117117+ */118118+//#define DEBUG_ENABLE_UCODE_MMIO_PRINT119119+120120+/* If you want to enable printing of PCI Config Space access, enable this */121121+//#define DEBUG_ENABLE_PCILOG122122+123123+124124+/* Detailed list maintained at:125125+ * http://openfacts.berlios.de/index-en.phtml?title=Bcm43xxDevices126126+ */127127+ static struct pci_device_id bcm43xx_pci_tbl[] = {128128+ /* Broadcom 4303 802.11b */129129+ { PCI_VENDOR_ID_BROADCOM, 0x4301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },130130+ /* Broadcom 4307 802.11b */131131+ { PCI_VENDOR_ID_BROADCOM, 0x4307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },132132+ /* Broadcom 4318 802.11b/g */133133+ { PCI_VENDOR_ID_BROADCOM, 0x4318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },134134+ /* Broadcom 4306 802.11b/g */135135+ { PCI_VENDOR_ID_BROADCOM, 0x4320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },136136+ /* Broadcom 4306 802.11a */137137+// { PCI_VENDOR_ID_BROADCOM, 0x4321, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },138138+ /* Broadcom 4309 802.11a/b/g */139139+ { PCI_VENDOR_ID_BROADCOM, 0x4324, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },140140+ /* Broadcom 43XG 802.11b/g */141141+ { PCI_VENDOR_ID_BROADCOM, 0x4325, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },142142+#ifdef CONFIG_BCM947XX143143+ /* SB bus on BCM947xx */144144+ { PCI_VENDOR_ID_BROADCOM, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },145145+#endif146146+ { 0 },147147+};148148+MODULE_DEVICE_TABLE(pci, bcm43xx_pci_tbl);149149+150150+static void bcm43xx_ram_write(struct bcm43xx_private *bcm, u16 offset, u32 val)151151+{152152+ u32 status;153153+154154+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);155155+ if (!(status & BCM43xx_SBF_XFER_REG_BYTESWAP))156156+ val = swab32(val);157157+158158+ bcm43xx_write32(bcm, BCM43xx_MMIO_RAM_CONTROL, offset);159159+ mmiowb();160160+ bcm43xx_write32(bcm, BCM43xx_MMIO_RAM_DATA, val);161161+}162162+163163+static inline164164+void bcm43xx_shm_control_word(struct bcm43xx_private *bcm,165165+ u16 routing, u16 offset)166166+{167167+ u32 control;168168+169169+ /* "offset" is the WORD offset. */170170+171171+ control = routing;172172+ control <<= 16;173173+ control |= offset;174174+ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_CONTROL, control);175175+}176176+177177+u32 bcm43xx_shm_read32(struct bcm43xx_private *bcm,178178+ u16 routing, u16 offset)179179+{180180+ u32 ret;181181+182182+ if (routing == BCM43xx_SHM_SHARED) {183183+ if (offset & 0x0003) {184184+ /* Unaligned access */185185+ bcm43xx_shm_control_word(bcm, routing, offset >> 2);186186+ ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED);187187+ ret <<= 16;188188+ bcm43xx_shm_control_word(bcm, routing, (offset >> 2) + 1);189189+ ret |= bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA);190190+191191+ return ret;192192+ }193193+ offset >>= 2;194194+ }195195+ bcm43xx_shm_control_word(bcm, routing, offset);196196+ ret = bcm43xx_read32(bcm, BCM43xx_MMIO_SHM_DATA);197197+198198+ return ret;199199+}200200+201201+u16 bcm43xx_shm_read16(struct bcm43xx_private *bcm,202202+ u16 routing, u16 offset)203203+{204204+ u16 ret;205205+206206+ if (routing == BCM43xx_SHM_SHARED) {207207+ if (offset & 0x0003) {208208+ /* Unaligned access */209209+ bcm43xx_shm_control_word(bcm, routing, offset >> 2);210210+ ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED);211211+212212+ return ret;213213+ }214214+ offset >>= 2;215215+ }216216+ bcm43xx_shm_control_word(bcm, routing, offset);217217+ ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA);218218+219219+ return ret;220220+}221221+222222+void bcm43xx_shm_write32(struct bcm43xx_private *bcm,223223+ u16 routing, u16 offset,224224+ u32 value)225225+{226226+ if (routing == BCM43xx_SHM_SHARED) {227227+ if (offset & 0x0003) {228228+ /* Unaligned access */229229+ bcm43xx_shm_control_word(bcm, routing, offset >> 2);230230+ mmiowb();231231+ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED,232232+ (value >> 16) & 0xffff);233233+ mmiowb();234234+ bcm43xx_shm_control_word(bcm, routing, (offset >> 2) + 1);235235+ mmiowb();236236+ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA,237237+ value & 0xffff);238238+ return;239239+ }240240+ offset >>= 2;241241+ }242242+ bcm43xx_shm_control_word(bcm, routing, offset);243243+ mmiowb();244244+ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, value);245245+}246246+247247+void bcm43xx_shm_write16(struct bcm43xx_private *bcm,248248+ u16 routing, u16 offset,249249+ u16 value)250250+{251251+ if (routing == BCM43xx_SHM_SHARED) {252252+ if (offset & 0x0003) {253253+ /* Unaligned access */254254+ bcm43xx_shm_control_word(bcm, routing, offset >> 2);255255+ mmiowb();256256+ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED,257257+ value);258258+ return;259259+ }260260+ offset >>= 2;261261+ }262262+ bcm43xx_shm_control_word(bcm, routing, offset);263263+ mmiowb();264264+ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA, value);265265+}266266+267267+void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf)268268+{269269+ /* We need to be careful. As we read the TSF from multiple270270+ * registers, we should take care of register overflows.271271+ * In theory, the whole tsf read process should be atomic.272272+ * We try to be atomic here, by restaring the read process,273273+ * if any of the high registers changed (overflew).274274+ */275275+ if (bcm->current_core->rev >= 3) {276276+ u32 low, high, high2;277277+278278+ do {279279+ high = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH);280280+ low = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW);281281+ high2 = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH);282282+ } while (unlikely(high != high2));283283+284284+ *tsf = high;285285+ *tsf <<= 32;286286+ *tsf |= low;287287+ } else {288288+ u64 tmp;289289+ u16 v0, v1, v2, v3;290290+ u16 test1, test2, test3;291291+292292+ do {293293+ v3 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_3);294294+ v2 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_2);295295+ v1 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_1);296296+ v0 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_0);297297+298298+ test3 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_3);299299+ test2 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_2);300300+ test1 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_1);301301+ } while (v3 != test3 || v2 != test2 || v1 != test1);302302+303303+ *tsf = v3;304304+ *tsf <<= 48;305305+ tmp = v2;306306+ tmp <<= 32;307307+ *tsf |= tmp;308308+ tmp = v1;309309+ tmp <<= 16;310310+ *tsf |= tmp;311311+ *tsf |= v0;312312+ }313313+}314314+315315+void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf)316316+{317317+ u32 status;318318+319319+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);320320+ status |= BCM43xx_SBF_TIME_UPDATE;321321+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);322322+ mmiowb();323323+324324+ /* Be careful with the in-progress timer.325325+ * First zero out the low register, so we have a full326326+ * register-overflow duration to complete the operation.327327+ */328328+ if (bcm->current_core->rev >= 3) {329329+ u32 lo = (tsf & 0x00000000FFFFFFFFULL);330330+ u32 hi = (tsf & 0xFFFFFFFF00000000ULL) >> 32;331331+332332+ bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW, 0);333333+ mmiowb();334334+ bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH, hi);335335+ mmiowb();336336+ bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW, lo);337337+ } else {338338+ u16 v0 = (tsf & 0x000000000000FFFFULL);339339+ u16 v1 = (tsf & 0x00000000FFFF0000ULL) >> 16;340340+ u16 v2 = (tsf & 0x0000FFFF00000000ULL) >> 32;341341+ u16 v3 = (tsf & 0xFFFF000000000000ULL) >> 48;342342+343343+ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_0, 0);344344+ mmiowb();345345+ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_3, v3);346346+ mmiowb();347347+ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_2, v2);348348+ mmiowb();349349+ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_1, v1);350350+ mmiowb();351351+ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_0, v0);352352+ }353353+354354+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);355355+ status &= ~BCM43xx_SBF_TIME_UPDATE;356356+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);357357+}358358+359359+static360360+void bcm43xx_macfilter_set(struct bcm43xx_private *bcm,361361+ u16 offset,362362+ const u8 *mac)363363+{364364+ u16 data;365365+366366+ offset |= 0x0020;367367+ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_CONTROL, offset);368368+369369+ data = mac[0];370370+ data |= mac[1] << 8;371371+ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data);372372+ data = mac[2];373373+ data |= mac[3] << 8;374374+ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data);375375+ data = mac[4];376376+ data |= mac[5] << 8;377377+ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data);378378+}379379+380380+static void bcm43xx_macfilter_clear(struct bcm43xx_private *bcm,381381+ u16 offset)382382+{383383+ const u8 zero_addr[ETH_ALEN] = { 0 };384384+385385+ bcm43xx_macfilter_set(bcm, offset, zero_addr);386386+}387387+388388+static void bcm43xx_write_mac_bssid_templates(struct bcm43xx_private *bcm)389389+{390390+ const u8 *mac = (const u8 *)(bcm->net_dev->dev_addr);391391+ const u8 *bssid = (const u8 *)(bcm->ieee->bssid);392392+ u8 mac_bssid[ETH_ALEN * 2];393393+ int i;394394+395395+ memcpy(mac_bssid, mac, ETH_ALEN);396396+ memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN);397397+398398+ /* Write our MAC address and BSSID to template ram */399399+ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32))400400+ bcm43xx_ram_write(bcm, 0x20 + i, *((u32 *)(mac_bssid + i)));401401+ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32))402402+ bcm43xx_ram_write(bcm, 0x78 + i, *((u32 *)(mac_bssid + i)));403403+ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32))404404+ bcm43xx_ram_write(bcm, 0x478 + i, *((u32 *)(mac_bssid + i)));405405+}406406+407407+//FIXME: Well, we should probably call them from somewhere.408408+#if 0409409+static void bcm43xx_set_slot_time(struct bcm43xx_private *bcm, u16 slot_time)410410+{411411+ /* slot_time is in usec. */412412+ if (bcm43xx_current_phy(bcm)->type != BCM43xx_PHYTYPE_G)413413+ return;414414+ bcm43xx_write16(bcm, 0x684, 510 + slot_time);415415+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0010, slot_time);416416+}417417+418418+static void bcm43xx_short_slot_timing_enable(struct bcm43xx_private *bcm)419419+{420420+ bcm43xx_set_slot_time(bcm, 9);421421+}422422+423423+static void bcm43xx_short_slot_timing_disable(struct bcm43xx_private *bcm)424424+{425425+ bcm43xx_set_slot_time(bcm, 20);426426+}427427+#endif428428+429429+/* FIXME: To get the MAC-filter working, we need to implement the430430+ * following functions (and rename them :)431431+ */432432+#if 0433433+static void bcm43xx_disassociate(struct bcm43xx_private *bcm)434434+{435435+ bcm43xx_mac_suspend(bcm);436436+ bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC);437437+438438+ bcm43xx_ram_write(bcm, 0x0026, 0x0000);439439+ bcm43xx_ram_write(bcm, 0x0028, 0x0000);440440+ bcm43xx_ram_write(bcm, 0x007E, 0x0000);441441+ bcm43xx_ram_write(bcm, 0x0080, 0x0000);442442+ bcm43xx_ram_write(bcm, 0x047E, 0x0000);443443+ bcm43xx_ram_write(bcm, 0x0480, 0x0000);444444+445445+ if (bcm->current_core->rev < 3) {446446+ bcm43xx_write16(bcm, 0x0610, 0x8000);447447+ bcm43xx_write16(bcm, 0x060E, 0x0000);448448+ } else449449+ bcm43xx_write32(bcm, 0x0188, 0x80000000);450450+451451+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff);452452+453453+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G &&454454+ ieee80211_is_ofdm_rate(bcm->softmac->txrates.default_rate))455455+ bcm43xx_short_slot_timing_enable(bcm);456456+457457+ bcm43xx_mac_enable(bcm);458458+}459459+460460+static void bcm43xx_associate(struct bcm43xx_private *bcm,461461+ const u8 *mac)462462+{463463+ memcpy(bcm->ieee->bssid, mac, ETH_ALEN);464464+465465+ bcm43xx_mac_suspend(bcm);466466+ bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_ASSOC, mac);467467+ bcm43xx_write_mac_bssid_templates(bcm);468468+ bcm43xx_mac_enable(bcm);469469+}470470+#endif471471+472472+/* Enable a Generic IRQ. "mask" is the mask of which IRQs to enable.473473+ * Returns the _previously_ enabled IRQ mask.474474+ */475475+static inline u32 bcm43xx_interrupt_enable(struct bcm43xx_private *bcm, u32 mask)476476+{477477+ u32 old_mask;478478+479479+ old_mask = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK);480480+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK, old_mask | mask);481481+482482+ return old_mask;483483+}484484+485485+/* Disable a Generic IRQ. "mask" is the mask of which IRQs to disable.486486+ * Returns the _previously_ enabled IRQ mask.487487+ */488488+static inline u32 bcm43xx_interrupt_disable(struct bcm43xx_private *bcm, u32 mask)489489+{490490+ u32 old_mask;491491+492492+ old_mask = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK);493493+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK, old_mask & ~mask);494494+495495+ return old_mask;496496+}497497+498498+/* Make sure we don't receive more data from the device. */499499+static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate)500500+{501501+ u32 old;502502+ unsigned long flags;503503+504504+ bcm43xx_lock_mmio(bcm, flags);505505+ if (bcm43xx_is_initializing(bcm) || bcm->shutting_down) {506506+ bcm43xx_unlock_mmio(bcm, flags);507507+ return -EBUSY;508508+ }509509+ old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);510510+ tasklet_disable(&bcm->isr_tasklet);511511+ bcm43xx_unlock_mmio(bcm, flags);512512+ if (oldstate)513513+ *oldstate = old;514514+515515+ return 0;516516+}517517+518518+static int bcm43xx_read_radioinfo(struct bcm43xx_private *bcm)519519+{520520+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);521521+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);522522+ u32 radio_id;523523+ u16 manufact;524524+ u16 version;525525+ u8 revision;526526+ s8 i;527527+528528+ if (bcm->chip_id == 0x4317) {529529+ if (bcm->chip_rev == 0x00)530530+ radio_id = 0x3205017F;531531+ else if (bcm->chip_rev == 0x01)532532+ radio_id = 0x4205017F;533533+ else534534+ radio_id = 0x5205017F;535535+ } else {536536+ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, BCM43xx_RADIOCTL_ID);537537+ radio_id = bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_HIGH);538538+ radio_id <<= 16;539539+ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, BCM43xx_RADIOCTL_ID);540540+ radio_id |= bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW);541541+ }542542+543543+ manufact = (radio_id & 0x00000FFF);544544+ version = (radio_id & 0x0FFFF000) >> 12;545545+ revision = (radio_id & 0xF0000000) >> 28;546546+547547+ dprintk(KERN_INFO PFX "Detected Radio: ID: %x (Manuf: %x Ver: %x Rev: %x)\n",548548+ radio_id, manufact, version, revision);549549+550550+ switch (phy->type) {551551+ case BCM43xx_PHYTYPE_A:552552+ if ((version != 0x2060) || (revision != 1) || (manufact != 0x17f))553553+ goto err_unsupported_radio;554554+ break;555555+ case BCM43xx_PHYTYPE_B:556556+ if ((version & 0xFFF0) != 0x2050)557557+ goto err_unsupported_radio;558558+ break;559559+ case BCM43xx_PHYTYPE_G:560560+ if (version != 0x2050)561561+ goto err_unsupported_radio;562562+ break;563563+ }564564+565565+ radio->manufact = manufact;566566+ radio->version = version;567567+ radio->revision = revision;568568+569569+ /* Set default attenuation values. */570570+ radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm);571571+ radio->radio_atten = bcm43xx_default_radio_attenuation(bcm);572572+ radio->txctl1 = bcm43xx_default_txctl1(bcm);573573+ radio->txctl2 = 0xFFFF;574574+ if (phy->type == BCM43xx_PHYTYPE_A)575575+ radio->txpower_desired = bcm->sprom.maxpower_aphy;576576+ else577577+ radio->txpower_desired = bcm->sprom.maxpower_bgphy;578578+579579+ /* Initialize the in-memory nrssi Lookup Table. */580580+ for (i = 0; i < 64; i++)581581+ radio->nrssi_lt[i] = i;582582+583583+ return 0;584584+585585+err_unsupported_radio:586586+ printk(KERN_ERR PFX "Unsupported Radio connected to the PHY!\n");587587+ return -ENODEV;588588+}589589+590590+static const char * bcm43xx_locale_iso(u8 locale)591591+{592592+ /* ISO 3166-1 country codes.593593+ * Note that there aren't ISO 3166-1 codes for594594+ * all or locales. (Not all locales are countries)595595+ */596596+ switch (locale) {597597+ case BCM43xx_LOCALE_WORLD:598598+ case BCM43xx_LOCALE_ALL:599599+ return "XX";600600+ case BCM43xx_LOCALE_THAILAND:601601+ return "TH";602602+ case BCM43xx_LOCALE_ISRAEL:603603+ return "IL";604604+ case BCM43xx_LOCALE_JORDAN:605605+ return "JO";606606+ case BCM43xx_LOCALE_CHINA:607607+ return "CN";608608+ case BCM43xx_LOCALE_JAPAN:609609+ case BCM43xx_LOCALE_JAPAN_HIGH:610610+ return "JP";611611+ case BCM43xx_LOCALE_USA_CANADA_ANZ:612612+ case BCM43xx_LOCALE_USA_LOW:613613+ return "US";614614+ case BCM43xx_LOCALE_EUROPE:615615+ return "EU";616616+ case BCM43xx_LOCALE_NONE:617617+ return " ";618618+ }619619+ assert(0);620620+ return " ";621621+}622622+623623+static const char * bcm43xx_locale_string(u8 locale)624624+{625625+ switch (locale) {626626+ case BCM43xx_LOCALE_WORLD:627627+ return "World";628628+ case BCM43xx_LOCALE_THAILAND:629629+ return "Thailand";630630+ case BCM43xx_LOCALE_ISRAEL:631631+ return "Israel";632632+ case BCM43xx_LOCALE_JORDAN:633633+ return "Jordan";634634+ case BCM43xx_LOCALE_CHINA:635635+ return "China";636636+ case BCM43xx_LOCALE_JAPAN:637637+ return "Japan";638638+ case BCM43xx_LOCALE_USA_CANADA_ANZ:639639+ return "USA/Canada/ANZ";640640+ case BCM43xx_LOCALE_EUROPE:641641+ return "Europe";642642+ case BCM43xx_LOCALE_USA_LOW:643643+ return "USAlow";644644+ case BCM43xx_LOCALE_JAPAN_HIGH:645645+ return "JapanHigh";646646+ case BCM43xx_LOCALE_ALL:647647+ return "All";648648+ case BCM43xx_LOCALE_NONE:649649+ return "None";650650+ }651651+ assert(0);652652+ return "";653653+}654654+655655+static inline u8 bcm43xx_crc8(u8 crc, u8 data)656656+{657657+ static const u8 t[] = {658658+ 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,659659+ 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,660660+ 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,661661+ 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,662662+ 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,663663+ 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,664664+ 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,665665+ 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,666666+ 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,667667+ 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,668668+ 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,669669+ 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,670670+ 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,671671+ 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,672672+ 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,673673+ 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,674674+ 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,675675+ 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,676676+ 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,677677+ 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,678678+ 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,679679+ 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,680680+ 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,681681+ 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,682682+ 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,683683+ 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,684684+ 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,685685+ 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,686686+ 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,687687+ 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,688688+ 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,689689+ 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,690690+ };691691+ return t[crc ^ data];692692+}693693+694694+static u8 bcm43xx_sprom_crc(const u16 *sprom)695695+{696696+ int word;697697+ u8 crc = 0xFF;698698+699699+ for (word = 0; word < BCM43xx_SPROM_SIZE - 1; word++) {700700+ crc = bcm43xx_crc8(crc, sprom[word] & 0x00FF);701701+ crc = bcm43xx_crc8(crc, (sprom[word] & 0xFF00) >> 8);702702+ }703703+ crc = bcm43xx_crc8(crc, sprom[BCM43xx_SPROM_VERSION] & 0x00FF);704704+ crc ^= 0xFF;705705+706706+ return crc;707707+}708708+709709+int bcm43xx_sprom_read(struct bcm43xx_private *bcm, u16 *sprom)710710+{711711+ int i;712712+ u8 crc, expected_crc;713713+714714+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++)715715+ sprom[i] = bcm43xx_read16(bcm, BCM43xx_SPROM_BASE + (i * 2));716716+ /* CRC-8 check. */717717+ crc = bcm43xx_sprom_crc(sprom);718718+ expected_crc = (sprom[BCM43xx_SPROM_VERSION] & 0xFF00) >> 8;719719+ if (crc != expected_crc) {720720+ printk(KERN_WARNING PFX "WARNING: Invalid SPROM checksum "721721+ "(0x%02X, expected: 0x%02X)\n",722722+ crc, expected_crc);723723+ return -EINVAL;724724+ }725725+726726+ return 0;727727+}728728+729729+int bcm43xx_sprom_write(struct bcm43xx_private *bcm, const u16 *sprom)730730+{731731+ int i, err;732732+ u8 crc, expected_crc;733733+ u32 spromctl;734734+735735+ /* CRC-8 validation of the input data. */736736+ crc = bcm43xx_sprom_crc(sprom);737737+ expected_crc = (sprom[BCM43xx_SPROM_VERSION] & 0xFF00) >> 8;738738+ if (crc != expected_crc) {739739+ printk(KERN_ERR PFX "SPROM input data: Invalid CRC\n");740740+ return -EINVAL;741741+ }742742+743743+ printk(KERN_INFO PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n");744744+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_SPROMCTL, &spromctl);745745+ if (err)746746+ goto err_ctlreg;747747+ spromctl |= 0x10; /* SPROM WRITE enable. */748748+ bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl);749749+ if (err)750750+ goto err_ctlreg;751751+ /* We must burn lots of CPU cycles here, but that does not752752+ * really matter as one does not write the SPROM every other minute...753753+ */754754+ printk(KERN_INFO PFX "[ 0%%");755755+ mdelay(500);756756+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {757757+ if (i == 16)758758+ printk("25%%");759759+ else if (i == 32)760760+ printk("50%%");761761+ else if (i == 48)762762+ printk("75%%");763763+ else if (i % 2)764764+ printk(".");765765+ bcm43xx_write16(bcm, BCM43xx_SPROM_BASE + (i * 2), sprom[i]);766766+ mmiowb();767767+ mdelay(20);768768+ }769769+ spromctl &= ~0x10; /* SPROM WRITE enable. */770770+ bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl);771771+ if (err)772772+ goto err_ctlreg;773773+ mdelay(500);774774+ printk("100%% ]\n");775775+ printk(KERN_INFO PFX "SPROM written.\n");776776+ bcm43xx_controller_restart(bcm, "SPROM update");777777+778778+ return 0;779779+err_ctlreg:780780+ printk(KERN_ERR PFX "Could not access SPROM control register.\n");781781+ return -ENODEV;782782+}783783+784784+static int bcm43xx_sprom_extract(struct bcm43xx_private *bcm)785785+{786786+ u16 value;787787+ u16 *sprom;788788+#ifdef CONFIG_BCM947XX789789+ char *c;790790+#endif791791+792792+ sprom = kzalloc(BCM43xx_SPROM_SIZE * sizeof(u16),793793+ GFP_KERNEL);794794+ if (!sprom) {795795+ printk(KERN_ERR PFX "sprom_extract OOM\n");796796+ return -ENOMEM;797797+ }798798+#ifdef CONFIG_BCM947XX799799+ sprom[BCM43xx_SPROM_BOARDFLAGS2] = atoi(nvram_get("boardflags2"));800800+ sprom[BCM43xx_SPROM_BOARDFLAGS] = atoi(nvram_get("boardflags"));801801+802802+ if ((c = nvram_get("il0macaddr")) != NULL)803803+ e_aton(c, (char *) &(sprom[BCM43xx_SPROM_IL0MACADDR]));804804+805805+ if ((c = nvram_get("et1macaddr")) != NULL)806806+ e_aton(c, (char *) &(sprom[BCM43xx_SPROM_ET1MACADDR]));807807+808808+ sprom[BCM43xx_SPROM_PA0B0] = atoi(nvram_get("pa0b0"));809809+ sprom[BCM43xx_SPROM_PA0B1] = atoi(nvram_get("pa0b1"));810810+ sprom[BCM43xx_SPROM_PA0B2] = atoi(nvram_get("pa0b2"));811811+812812+ sprom[BCM43xx_SPROM_PA1B0] = atoi(nvram_get("pa1b0"));813813+ sprom[BCM43xx_SPROM_PA1B1] = atoi(nvram_get("pa1b1"));814814+ sprom[BCM43xx_SPROM_PA1B2] = atoi(nvram_get("pa1b2"));815815+816816+ sprom[BCM43xx_SPROM_BOARDREV] = atoi(nvram_get("boardrev"));817817+#else818818+ bcm43xx_sprom_read(bcm, sprom);819819+#endif820820+821821+ /* boardflags2 */822822+ value = sprom[BCM43xx_SPROM_BOARDFLAGS2];823823+ bcm->sprom.boardflags2 = value;824824+825825+ /* il0macaddr */826826+ value = sprom[BCM43xx_SPROM_IL0MACADDR + 0];827827+ *(((u16 *)bcm->sprom.il0macaddr) + 0) = cpu_to_be16(value);828828+ value = sprom[BCM43xx_SPROM_IL0MACADDR + 1];829829+ *(((u16 *)bcm->sprom.il0macaddr) + 1) = cpu_to_be16(value);830830+ value = sprom[BCM43xx_SPROM_IL0MACADDR + 2];831831+ *(((u16 *)bcm->sprom.il0macaddr) + 2) = cpu_to_be16(value);832832+833833+ /* et0macaddr */834834+ value = sprom[BCM43xx_SPROM_ET0MACADDR + 0];835835+ *(((u16 *)bcm->sprom.et0macaddr) + 0) = cpu_to_be16(value);836836+ value = sprom[BCM43xx_SPROM_ET0MACADDR + 1];837837+ *(((u16 *)bcm->sprom.et0macaddr) + 1) = cpu_to_be16(value);838838+ value = sprom[BCM43xx_SPROM_ET0MACADDR + 2];839839+ *(((u16 *)bcm->sprom.et0macaddr) + 2) = cpu_to_be16(value);840840+841841+ /* et1macaddr */842842+ value = sprom[BCM43xx_SPROM_ET1MACADDR + 0];843843+ *(((u16 *)bcm->sprom.et1macaddr) + 0) = cpu_to_be16(value);844844+ value = sprom[BCM43xx_SPROM_ET1MACADDR + 1];845845+ *(((u16 *)bcm->sprom.et1macaddr) + 1) = cpu_to_be16(value);846846+ value = sprom[BCM43xx_SPROM_ET1MACADDR + 2];847847+ *(((u16 *)bcm->sprom.et1macaddr) + 2) = cpu_to_be16(value);848848+849849+ /* ethernet phy settings */850850+ value = sprom[BCM43xx_SPROM_ETHPHY];851851+ bcm->sprom.et0phyaddr = (value & 0x001F);852852+ bcm->sprom.et1phyaddr = (value & 0x03E0) >> 5;853853+ bcm->sprom.et0mdcport = (value & (1 << 14)) >> 14;854854+ bcm->sprom.et1mdcport = (value & (1 << 15)) >> 15;855855+856856+ /* boardrev, antennas, locale */857857+ value = sprom[BCM43xx_SPROM_BOARDREV];858858+ bcm->sprom.boardrev = (value & 0x00FF);859859+ bcm->sprom.locale = (value & 0x0F00) >> 8;860860+ bcm->sprom.antennas_aphy = (value & 0x3000) >> 12;861861+ bcm->sprom.antennas_bgphy = (value & 0xC000) >> 14;862862+ if (modparam_locale != -1) {863863+ if (modparam_locale >= 0 && modparam_locale <= 11) {864864+ bcm->sprom.locale = modparam_locale;865865+ printk(KERN_WARNING PFX "Operating with modified "866866+ "LocaleCode %u (%s)\n",867867+ bcm->sprom.locale,868868+ bcm43xx_locale_string(bcm->sprom.locale));869869+ } else {870870+ printk(KERN_WARNING PFX "Module parameter \"locale\" "871871+ "invalid value. (0 - 11)\n");872872+ }873873+ }874874+875875+ /* pa0b* */876876+ value = sprom[BCM43xx_SPROM_PA0B0];877877+ bcm->sprom.pa0b0 = value;878878+ value = sprom[BCM43xx_SPROM_PA0B1];879879+ bcm->sprom.pa0b1 = value;880880+ value = sprom[BCM43xx_SPROM_PA0B2];881881+ bcm->sprom.pa0b2 = value;882882+883883+ /* wl0gpio* */884884+ value = sprom[BCM43xx_SPROM_WL0GPIO0];885885+ if (value == 0x0000)886886+ value = 0xFFFF;887887+ bcm->sprom.wl0gpio0 = value & 0x00FF;888888+ bcm->sprom.wl0gpio1 = (value & 0xFF00) >> 8;889889+ value = sprom[BCM43xx_SPROM_WL0GPIO2];890890+ if (value == 0x0000)891891+ value = 0xFFFF;892892+ bcm->sprom.wl0gpio2 = value & 0x00FF;893893+ bcm->sprom.wl0gpio3 = (value & 0xFF00) >> 8;894894+895895+ /* maxpower */896896+ value = sprom[BCM43xx_SPROM_MAXPWR];897897+ bcm->sprom.maxpower_aphy = (value & 0xFF00) >> 8;898898+ bcm->sprom.maxpower_bgphy = value & 0x00FF;899899+900900+ /* pa1b* */901901+ value = sprom[BCM43xx_SPROM_PA1B0];902902+ bcm->sprom.pa1b0 = value;903903+ value = sprom[BCM43xx_SPROM_PA1B1];904904+ bcm->sprom.pa1b1 = value;905905+ value = sprom[BCM43xx_SPROM_PA1B2];906906+ bcm->sprom.pa1b2 = value;907907+908908+ /* idle tssi target */909909+ value = sprom[BCM43xx_SPROM_IDL_TSSI_TGT];910910+ bcm->sprom.idle_tssi_tgt_aphy = value & 0x00FF;911911+ bcm->sprom.idle_tssi_tgt_bgphy = (value & 0xFF00) >> 8;912912+913913+ /* boardflags */914914+ value = sprom[BCM43xx_SPROM_BOARDFLAGS];915915+ if (value == 0xFFFF)916916+ value = 0x0000;917917+ bcm->sprom.boardflags = value;918918+ /* boardflags workarounds */919919+ if (bcm->board_vendor == PCI_VENDOR_ID_DELL &&920920+ bcm->chip_id == 0x4301 &&921921+ bcm->board_revision == 0x74)922922+ bcm->sprom.boardflags |= BCM43xx_BFL_BTCOEXIST;923923+ if (bcm->board_vendor == PCI_VENDOR_ID_APPLE &&924924+ bcm->board_type == 0x4E &&925925+ bcm->board_revision > 0x40)926926+ bcm->sprom.boardflags |= BCM43xx_BFL_PACTRL;927927+928928+ /* antenna gain */929929+ value = sprom[BCM43xx_SPROM_ANTENNA_GAIN];930930+ if (value == 0x0000 || value == 0xFFFF)931931+ value = 0x0202;932932+ /* convert values to Q5.2 */933933+ bcm->sprom.antennagain_aphy = ((value & 0xFF00) >> 8) * 4;934934+ bcm->sprom.antennagain_bgphy = (value & 0x00FF) * 4;935935+936936+ kfree(sprom);937937+938938+ return 0;939939+}940940+941941+static void bcm43xx_geo_init(struct bcm43xx_private *bcm)942942+{943943+ struct ieee80211_geo geo;944944+ struct ieee80211_channel *chan;945945+ int have_a = 0, have_bg = 0;946946+ int i;947947+ u8 channel;948948+ struct bcm43xx_phyinfo *phy;949949+ const char *iso_country;950950+951951+ memset(&geo, 0, sizeof(geo));952952+ for (i = 0; i < bcm->nr_80211_available; i++) {953953+ phy = &(bcm->core_80211_ext[i].phy);954954+ switch (phy->type) {955955+ case BCM43xx_PHYTYPE_B:956956+ case BCM43xx_PHYTYPE_G:957957+ have_bg = 1;958958+ break;959959+ case BCM43xx_PHYTYPE_A:960960+ have_a = 1;961961+ break;962962+ default:963963+ assert(0);964964+ }965965+ }966966+ iso_country = bcm43xx_locale_iso(bcm->sprom.locale);967967+968968+ if (have_a) {969969+ for (i = 0, channel = 0; channel < 201; channel++) {970970+ chan = &geo.a[i++];971971+ chan->freq = bcm43xx_channel_to_freq_a(channel);972972+ chan->channel = channel;973973+ }974974+ geo.a_channels = i;975975+ }976976+ if (have_bg) {977977+ for (i = 0, channel = 1; channel < 15; channel++) {978978+ chan = &geo.bg[i++];979979+ chan->freq = bcm43xx_channel_to_freq_bg(channel);980980+ chan->channel = channel;981981+ }982982+ geo.bg_channels = i;983983+ }984984+ memcpy(geo.name, iso_country, 2);985985+ if (0 /*TODO: Outdoor use only */)986986+ geo.name[2] = 'O';987987+ else if (0 /*TODO: Indoor use only */)988988+ geo.name[2] = 'I';989989+ else990990+ geo.name[2] = ' ';991991+ geo.name[3] = '\0';992992+993993+ ieee80211_set_geo(bcm->ieee, &geo);994994+}995995+996996+/* DummyTransmission function, as documented on 997997+ * http://bcm-specs.sipsolutions.net/DummyTransmission998998+ */999999+void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm)10001000+{10011001+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);10021002+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);10031003+ unsigned int i, max_loop;10041004+ u16 value = 0;10051005+ u32 buffer[5] = {10061006+ 0x00000000,10071007+ 0x0000D400,10081008+ 0x00000000,10091009+ 0x00000001,10101010+ 0x00000000,10111011+ };10121012+10131013+ switch (phy->type) {10141014+ case BCM43xx_PHYTYPE_A:10151015+ max_loop = 0x1E;10161016+ buffer[0] = 0xCC010200;10171017+ break;10181018+ case BCM43xx_PHYTYPE_B:10191019+ case BCM43xx_PHYTYPE_G:10201020+ max_loop = 0xFA;10211021+ buffer[0] = 0x6E840B00; 10221022+ break;10231023+ default:10241024+ assert(0);10251025+ return;10261026+ }10271027+10281028+ for (i = 0; i < 5; i++)10291029+ bcm43xx_ram_write(bcm, i * 4, buffer[i]);10301030+10311031+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */10321032+10331033+ bcm43xx_write16(bcm, 0x0568, 0x0000);10341034+ bcm43xx_write16(bcm, 0x07C0, 0x0000);10351035+ bcm43xx_write16(bcm, 0x050C, ((phy->type == BCM43xx_PHYTYPE_A) ? 1 : 0));10361036+ bcm43xx_write16(bcm, 0x0508, 0x0000);10371037+ bcm43xx_write16(bcm, 0x050A, 0x0000);10381038+ bcm43xx_write16(bcm, 0x054C, 0x0000);10391039+ bcm43xx_write16(bcm, 0x056A, 0x0014);10401040+ bcm43xx_write16(bcm, 0x0568, 0x0826);10411041+ bcm43xx_write16(bcm, 0x0500, 0x0000);10421042+ bcm43xx_write16(bcm, 0x0502, 0x0030);10431043+10441044+ if (radio->version == 0x2050 && radio->revision <= 0x5)10451045+ bcm43xx_radio_write16(bcm, 0x0051, 0x0017);10461046+ for (i = 0x00; i < max_loop; i++) {10471047+ value = bcm43xx_read16(bcm, 0x050E);10481048+ if (value & 0x0080)10491049+ break;10501050+ udelay(10);10511051+ }10521052+ for (i = 0x00; i < 0x0A; i++) {10531053+ value = bcm43xx_read16(bcm, 0x050E);10541054+ if (value & 0x0400)10551055+ break;10561056+ udelay(10);10571057+ }10581058+ for (i = 0x00; i < 0x0A; i++) {10591059+ value = bcm43xx_read16(bcm, 0x0690);10601060+ if (!(value & 0x0100))10611061+ break;10621062+ udelay(10);10631063+ }10641064+ if (radio->version == 0x2050 && radio->revision <= 0x5)10651065+ bcm43xx_radio_write16(bcm, 0x0051, 0x0037);10661066+}10671067+10681068+static void key_write(struct bcm43xx_private *bcm,10691069+ u8 index, u8 algorithm, const u16 *key)10701070+{10711071+ unsigned int i, basic_wep = 0;10721072+ u32 offset;10731073+ u16 value;10741074+10751075+ /* Write associated key information */10761076+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x100 + (index * 2),10771077+ ((index << 4) | (algorithm & 0x0F)));10781078+10791079+ /* The first 4 WEP keys need extra love */10801080+ if (((algorithm == BCM43xx_SEC_ALGO_WEP) ||10811081+ (algorithm == BCM43xx_SEC_ALGO_WEP104)) && (index < 4))10821082+ basic_wep = 1;10831083+10841084+ /* Write key payload, 8 little endian words */10851085+ offset = bcm->security_offset + (index * BCM43xx_SEC_KEYSIZE);10861086+ for (i = 0; i < (BCM43xx_SEC_KEYSIZE / sizeof(u16)); i++) {10871087+ value = cpu_to_le16(key[i]);10881088+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED,10891089+ offset + (i * 2), value);10901090+10911091+ if (!basic_wep)10921092+ continue;10931093+10941094+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED,10951095+ offset + (i * 2) + 4 * BCM43xx_SEC_KEYSIZE,10961096+ value);10971097+ }10981098+}10991099+11001100+static void keymac_write(struct bcm43xx_private *bcm,11011101+ u8 index, const u32 *addr)11021102+{11031103+ /* for keys 0-3 there is no associated mac address */11041104+ if (index < 4)11051105+ return;11061106+11071107+ index -= 4;11081108+ if (bcm->current_core->rev >= 5) {11091109+ bcm43xx_shm_write32(bcm,11101110+ BCM43xx_SHM_HWMAC,11111111+ index * 2,11121112+ cpu_to_be32(*addr));11131113+ bcm43xx_shm_write16(bcm,11141114+ BCM43xx_SHM_HWMAC,11151115+ (index * 2) + 1,11161116+ cpu_to_be16(*((u16 *)(addr + 1))));11171117+ } else {11181118+ if (index < 8) {11191119+ TODO(); /* Put them in the macaddress filter */11201120+ } else {11211121+ TODO();11221122+ /* Put them BCM43xx_SHM_SHARED, stating index 0x0120.11231123+ Keep in mind to update the count of keymacs in 0x003E as well! */11241124+ }11251125+ }11261126+}11271127+11281128+static int bcm43xx_key_write(struct bcm43xx_private *bcm,11291129+ u8 index, u8 algorithm,11301130+ const u8 *_key, int key_len,11311131+ const u8 *mac_addr)11321132+{11331133+ u8 key[BCM43xx_SEC_KEYSIZE] = { 0 };11341134+11351135+ if (index >= ARRAY_SIZE(bcm->key))11361136+ return -EINVAL;11371137+ if (key_len > ARRAY_SIZE(key))11381138+ return -EINVAL;11391139+ if (algorithm < 1 || algorithm > 5)11401140+ return -EINVAL;11411141+11421142+ memcpy(key, _key, key_len);11431143+ key_write(bcm, index, algorithm, (const u16 *)key);11441144+ keymac_write(bcm, index, (const u32 *)mac_addr);11451145+11461146+ bcm->key[index].algorithm = algorithm;11471147+11481148+ return 0;11491149+}11501150+11511151+static void bcm43xx_clear_keys(struct bcm43xx_private *bcm)11521152+{11531153+ static const u32 zero_mac[2] = { 0 };11541154+ unsigned int i,j, nr_keys = 54;11551155+ u16 offset;11561156+11571157+ if (bcm->current_core->rev < 5)11581158+ nr_keys = 16;11591159+ assert(nr_keys <= ARRAY_SIZE(bcm->key));11601160+11611161+ for (i = 0; i < nr_keys; i++) {11621162+ bcm->key[i].enabled = 0;11631163+ /* returns for i < 4 immediately */11641164+ keymac_write(bcm, i, zero_mac);11651165+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED,11661166+ 0x100 + (i * 2), 0x0000);11671167+ for (j = 0; j < 8; j++) {11681168+ offset = bcm->security_offset + (j * 4) + (i * BCM43xx_SEC_KEYSIZE);11691169+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED,11701170+ offset, 0x0000);11711171+ }11721172+ }11731173+ dprintk(KERN_INFO PFX "Keys cleared\n");11741174+}11751175+11761176+/* Lowlevel core-switch function. This is only to be used in11771177+ * bcm43xx_switch_core() and bcm43xx_probe_cores()11781178+ */11791179+static int _switch_core(struct bcm43xx_private *bcm, int core)11801180+{11811181+ int err;11821182+ int attempts = 0;11831183+ u32 current_core;11841184+11851185+ assert(core >= 0);11861186+ while (1) {11871187+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_ACTIVE_CORE,11881188+ (core * 0x1000) + 0x18000000);11891189+ if (unlikely(err))11901190+ goto error;11911191+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_ACTIVE_CORE,11921192+ ¤t_core);11931193+ if (unlikely(err))11941194+ goto error;11951195+ current_core = (current_core - 0x18000000) / 0x1000;11961196+ if (current_core == core)11971197+ break;11981198+11991199+ if (unlikely(attempts++ > BCM43xx_SWITCH_CORE_MAX_RETRIES))12001200+ goto error;12011201+ udelay(10);12021202+ }12031203+#ifdef CONFIG_BCM947XX12041204+ if (bcm->pci_dev->bus->number == 0)12051205+ bcm->current_core_offset = 0x1000 * core;12061206+ else12071207+ bcm->current_core_offset = 0;12081208+#endif12091209+12101210+ return 0;12111211+error:12121212+ printk(KERN_ERR PFX "Failed to switch to core %d\n", core);12131213+ return -ENODEV;12141214+}12151215+12161216+int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core)12171217+{12181218+ int err;12191219+12201220+ if (unlikely(!new_core))12211221+ return 0;12221222+ if (!new_core->available)12231223+ return -ENODEV;12241224+ if (bcm->current_core == new_core)12251225+ return 0;12261226+ err = _switch_core(bcm, new_core->index);12271227+ if (unlikely(err))12281228+ goto out;12291229+12301230+ bcm->current_core = new_core;12311231+ bcm->current_80211_core_idx = -1;12321232+ if (new_core->id == BCM43xx_COREID_80211)12331233+ bcm->current_80211_core_idx = (int)(new_core - &(bcm->core_80211[0]));12341234+12351235+out:12361236+ return err;12371237+}12381238+12391239+static int bcm43xx_core_enabled(struct bcm43xx_private *bcm)12401240+{12411241+ u32 value;12421242+12431243+ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);12441244+ value &= BCM43xx_SBTMSTATELOW_CLOCK | BCM43xx_SBTMSTATELOW_RESET12451245+ | BCM43xx_SBTMSTATELOW_REJECT;12461246+12471247+ return (value == BCM43xx_SBTMSTATELOW_CLOCK);12481248+}12491249+12501250+/* disable current core */12511251+static int bcm43xx_core_disable(struct bcm43xx_private *bcm, u32 core_flags)12521252+{12531253+ u32 sbtmstatelow;12541254+ u32 sbtmstatehigh;12551255+ int i;12561256+12571257+ /* fetch sbtmstatelow from core information registers */12581258+ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);12591259+12601260+ /* core is already in reset */12611261+ if (sbtmstatelow & BCM43xx_SBTMSTATELOW_RESET)12621262+ goto out;12631263+12641264+ if (sbtmstatelow & BCM43xx_SBTMSTATELOW_CLOCK) {12651265+ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK |12661266+ BCM43xx_SBTMSTATELOW_REJECT;12671267+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);12681268+12691269+ for (i = 0; i < 1000; i++) {12701270+ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);12711271+ if (sbtmstatelow & BCM43xx_SBTMSTATELOW_REJECT) {12721272+ i = -1;12731273+ break;12741274+ }12751275+ udelay(10);12761276+ }12771277+ if (i != -1) {12781278+ printk(KERN_ERR PFX "Error: core_disable() REJECT timeout!\n");12791279+ return -EBUSY;12801280+ }12811281+12821282+ for (i = 0; i < 1000; i++) {12831283+ sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH);12841284+ if (!(sbtmstatehigh & BCM43xx_SBTMSTATEHIGH_BUSY)) {12851285+ i = -1;12861286+ break;12871287+ }12881288+ udelay(10);12891289+ }12901290+ if (i != -1) {12911291+ printk(KERN_ERR PFX "Error: core_disable() BUSY timeout!\n");12921292+ return -EBUSY;12931293+ }12941294+12951295+ sbtmstatelow = BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK |12961296+ BCM43xx_SBTMSTATELOW_REJECT |12971297+ BCM43xx_SBTMSTATELOW_RESET |12981298+ BCM43xx_SBTMSTATELOW_CLOCK |12991299+ core_flags;13001300+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);13011301+ udelay(10);13021302+ }13031303+13041304+ sbtmstatelow = BCM43xx_SBTMSTATELOW_RESET |13051305+ BCM43xx_SBTMSTATELOW_REJECT |13061306+ core_flags;13071307+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);13081308+13091309+out:13101310+ bcm->current_core->enabled = 0;13111311+13121312+ return 0;13131313+}13141314+13151315+/* enable (reset) current core */13161316+static int bcm43xx_core_enable(struct bcm43xx_private *bcm, u32 core_flags)13171317+{13181318+ u32 sbtmstatelow;13191319+ u32 sbtmstatehigh;13201320+ u32 sbimstate;13211321+ int err;13221322+13231323+ err = bcm43xx_core_disable(bcm, core_flags);13241324+ if (err)13251325+ goto out;13261326+13271327+ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK |13281328+ BCM43xx_SBTMSTATELOW_RESET |13291329+ BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK |13301330+ core_flags;13311331+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);13321332+ udelay(1);13331333+13341334+ sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH);13351335+ if (sbtmstatehigh & BCM43xx_SBTMSTATEHIGH_SERROR) {13361336+ sbtmstatehigh = 0x00000000;13371337+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATEHIGH, sbtmstatehigh);13381338+ }13391339+13401340+ sbimstate = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMSTATE);13411341+ if (sbimstate & (BCM43xx_SBIMSTATE_IB_ERROR | BCM43xx_SBIMSTATE_TIMEOUT)) {13421342+ sbimstate &= ~(BCM43xx_SBIMSTATE_IB_ERROR | BCM43xx_SBIMSTATE_TIMEOUT);13431343+ bcm43xx_write32(bcm, BCM43xx_CIR_SBIMSTATE, sbimstate);13441344+ }13451345+13461346+ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK |13471347+ BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK |13481348+ core_flags;13491349+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);13501350+ udelay(1);13511351+13521352+ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK | core_flags;13531353+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);13541354+ udelay(1);13551355+13561356+ bcm->current_core->enabled = 1;13571357+ assert(err == 0);13581358+out:13591359+ return err;13601360+}13611361+13621362+/* http://bcm-specs.sipsolutions.net/80211CoreReset */13631363+void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy)13641364+{13651365+ u32 flags = 0x00040000;13661366+13671367+ if ((bcm43xx_core_enabled(bcm)) &&13681368+ !bcm43xx_using_pio(bcm)) {13691369+//FIXME: Do we _really_ want #ifndef CONFIG_BCM947XX here?13701370+#ifndef CONFIG_BCM947XX13711371+ /* reset all used DMA controllers. */13721372+ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA1_BASE);13731373+ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA2_BASE);13741374+ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA3_BASE);13751375+ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA4_BASE);13761376+ bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA1_BASE);13771377+ if (bcm->current_core->rev < 5)13781378+ bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA4_BASE);13791379+#endif13801380+ }13811381+ if (bcm->shutting_down) {13821382+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,13831383+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)13841384+ & ~(BCM43xx_SBF_MAC_ENABLED | 0x00000002));13851385+ } else {13861386+ if (connect_phy)13871387+ flags |= 0x20000000;13881388+ bcm43xx_phy_connect(bcm, connect_phy);13891389+ bcm43xx_core_enable(bcm, flags);13901390+ bcm43xx_write16(bcm, 0x03E6, 0x0000);13911391+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,13921392+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)13931393+ | BCM43xx_SBF_400);13941394+ }13951395+}13961396+13971397+static void bcm43xx_wireless_core_disable(struct bcm43xx_private *bcm)13981398+{13991399+ bcm43xx_radio_turn_off(bcm);14001400+ bcm43xx_write16(bcm, 0x03E6, 0x00F4);14011401+ bcm43xx_core_disable(bcm, 0);14021402+}14031403+14041404+/* Mark the current 80211 core inactive.14051405+ * "active_80211_core" is the other 80211 core, which is used.14061406+ */14071407+static int bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm,14081408+ struct bcm43xx_coreinfo *active_80211_core)14091409+{14101410+ u32 sbtmstatelow;14111411+ struct bcm43xx_coreinfo *old_core;14121412+ int err = 0;14131413+14141414+ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);14151415+ bcm43xx_radio_turn_off(bcm);14161416+ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);14171417+ sbtmstatelow &= ~0x200a0000;14181418+ sbtmstatelow |= 0xa0000;14191419+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);14201420+ udelay(1);14211421+ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);14221422+ sbtmstatelow &= ~0xa0000;14231423+ sbtmstatelow |= 0x80000;14241424+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);14251425+ udelay(1);14261426+14271427+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) {14281428+ old_core = bcm->current_core;14291429+ err = bcm43xx_switch_core(bcm, active_80211_core);14301430+ if (err)14311431+ goto out;14321432+ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);14331433+ sbtmstatelow &= ~0x20000000;14341434+ sbtmstatelow |= 0x20000000;14351435+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);14361436+ err = bcm43xx_switch_core(bcm, old_core);14371437+ }14381438+14391439+out:14401440+ return err;14411441+}14421442+14431443+static void handle_irq_transmit_status(struct bcm43xx_private *bcm)14441444+{14451445+ u32 v0, v1;14461446+ u16 tmp;14471447+ struct bcm43xx_xmitstatus stat;14481448+14491449+ while (1) {14501450+ v0 = bcm43xx_read32(bcm, BCM43xx_MMIO_XMITSTAT_0);14511451+ if (!v0)14521452+ break;14531453+ v1 = bcm43xx_read32(bcm, BCM43xx_MMIO_XMITSTAT_1);14541454+14551455+ stat.cookie = (v0 >> 16) & 0x0000FFFF;14561456+ tmp = (u16)((v0 & 0xFFF0) | ((v0 & 0xF) >> 1));14571457+ stat.flags = tmp & 0xFF;14581458+ stat.cnt1 = (tmp & 0x0F00) >> 8;14591459+ stat.cnt2 = (tmp & 0xF000) >> 12;14601460+ stat.seq = (u16)(v1 & 0xFFFF);14611461+ stat.unknown = (u16)((v1 >> 16) & 0xFF);14621462+14631463+ bcm43xx_debugfs_log_txstat(bcm, &stat);14641464+14651465+ if (stat.flags & BCM43xx_TXSTAT_FLAG_IGNORE)14661466+ continue;14671467+ if (!(stat.flags & BCM43xx_TXSTAT_FLAG_ACK)) {14681468+ //TODO: packet was not acked (was lost)14691469+ }14701470+ //TODO: There are more (unknown) flags to test. see bcm43xx_main.h14711471+14721472+ if (bcm43xx_using_pio(bcm))14731473+ bcm43xx_pio_handle_xmitstatus(bcm, &stat);14741474+ else14751475+ bcm43xx_dma_handle_xmitstatus(bcm, &stat);14761476+ }14771477+}14781478+14791479+static void bcm43xx_generate_noise_sample(struct bcm43xx_private *bcm)14801480+{14811481+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x408, 0x7F7F);14821482+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x40A, 0x7F7F);14831483+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD,14841484+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD) | (1 << 4));14851485+ assert(bcm->noisecalc.core_at_start == bcm->current_core);14861486+ assert(bcm->noisecalc.channel_at_start == bcm43xx_current_radio(bcm)->channel);14871487+}14881488+14891489+static void bcm43xx_calculate_link_quality(struct bcm43xx_private *bcm)14901490+{14911491+ /* Top half of Link Quality calculation. */14921492+14931493+ if (bcm->noisecalc.calculation_running)14941494+ return;14951495+ bcm->noisecalc.core_at_start = bcm->current_core;14961496+ bcm->noisecalc.channel_at_start = bcm43xx_current_radio(bcm)->channel;14971497+ bcm->noisecalc.calculation_running = 1;14981498+ bcm->noisecalc.nr_samples = 0;14991499+15001500+ bcm43xx_generate_noise_sample(bcm);15011501+}15021502+15031503+static void handle_irq_noise(struct bcm43xx_private *bcm)15041504+{15051505+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);15061506+ u16 tmp;15071507+ u8 noise[4];15081508+ u8 i, j;15091509+ s32 average;15101510+15111511+ /* Bottom half of Link Quality calculation. */15121512+15131513+ assert(bcm->noisecalc.calculation_running);15141514+ if (bcm->noisecalc.core_at_start != bcm->current_core ||15151515+ bcm->noisecalc.channel_at_start != radio->channel)15161516+ goto drop_calculation;15171517+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x408);15181518+ noise[0] = (tmp & 0x00FF);15191519+ noise[1] = (tmp & 0xFF00) >> 8;15201520+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x40A);15211521+ noise[2] = (tmp & 0x00FF);15221522+ noise[3] = (tmp & 0xFF00) >> 8;15231523+ if (noise[0] == 0x7F || noise[1] == 0x7F ||15241524+ noise[2] == 0x7F || noise[3] == 0x7F)15251525+ goto generate_new;15261526+15271527+ /* Get the noise samples. */15281528+ assert(bcm->noisecalc.nr_samples <= 8);15291529+ i = bcm->noisecalc.nr_samples;15301530+ noise[0] = limit_value(noise[0], 0, ARRAY_SIZE(radio->nrssi_lt) - 1);15311531+ noise[1] = limit_value(noise[1], 0, ARRAY_SIZE(radio->nrssi_lt) - 1);15321532+ noise[2] = limit_value(noise[2], 0, ARRAY_SIZE(radio->nrssi_lt) - 1);15331533+ noise[3] = limit_value(noise[3], 0, ARRAY_SIZE(radio->nrssi_lt) - 1);15341534+ bcm->noisecalc.samples[i][0] = radio->nrssi_lt[noise[0]];15351535+ bcm->noisecalc.samples[i][1] = radio->nrssi_lt[noise[1]];15361536+ bcm->noisecalc.samples[i][2] = radio->nrssi_lt[noise[2]];15371537+ bcm->noisecalc.samples[i][3] = radio->nrssi_lt[noise[3]];15381538+ bcm->noisecalc.nr_samples++;15391539+ if (bcm->noisecalc.nr_samples == 8) {15401540+ /* Calculate the Link Quality by the noise samples. */15411541+ average = 0;15421542+ for (i = 0; i < 8; i++) {15431543+ for (j = 0; j < 4; j++)15441544+ average += bcm->noisecalc.samples[i][j];15451545+ }15461546+ average /= (8 * 4);15471547+ average *= 125;15481548+ average += 64;15491549+ average /= 128;15501550+15511551+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x40C);15521552+ tmp = (tmp / 128) & 0x1F;15531553+ if (tmp >= 8)15541554+ average += 2;15551555+ else15561556+ average -= 25;15571557+ if (tmp == 8)15581558+ average -= 72;15591559+ else15601560+ average -= 48;15611561+15621562+/* FIXME: This is wrong, but people want fancy stats. well... */15631563+bcm->stats.noise = average;15641564+ if (average > -65)15651565+ bcm->stats.link_quality = 0;15661566+ else if (average > -75)15671567+ bcm->stats.link_quality = 1;15681568+ else if (average > -85)15691569+ bcm->stats.link_quality = 2;15701570+ else15711571+ bcm->stats.link_quality = 3;15721572+// dprintk(KERN_INFO PFX "Link Quality: %u (avg was %d)\n", bcm->stats.link_quality, average);15731573+drop_calculation:15741574+ bcm->noisecalc.calculation_running = 0;15751575+ return;15761576+ }15771577+generate_new:15781578+ bcm43xx_generate_noise_sample(bcm);15791579+}15801580+15811581+static void handle_irq_ps(struct bcm43xx_private *bcm)15821582+{15831583+ if (bcm->ieee->iw_mode == IW_MODE_MASTER) {15841584+ ///TODO: PS TBTT15851585+ } else {15861586+ if (1/*FIXME: the last PSpoll frame was sent successfully */)15871587+ bcm43xx_power_saving_ctl_bits(bcm, -1, -1);15881588+ }15891589+ if (bcm->ieee->iw_mode == IW_MODE_ADHOC)15901590+ bcm->reg124_set_0x4 = 1;15911591+ //FIXME else set to false?15921592+}15931593+15941594+static void handle_irq_reg124(struct bcm43xx_private *bcm)15951595+{15961596+ if (!bcm->reg124_set_0x4)15971597+ return;15981598+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD,15991599+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD)16001600+ | 0x4);16011601+ //FIXME: reset reg124_set_0x4 to false?16021602+}16031603+16041604+static void handle_irq_pmq(struct bcm43xx_private *bcm)16051605+{16061606+ u32 tmp;16071607+16081608+ //TODO: AP mode.16091609+16101610+ while (1) {16111611+ tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_PS_STATUS);16121612+ if (!(tmp & 0x00000008))16131613+ break;16141614+ }16151615+ /* 16bit write is odd, but correct. */16161616+ bcm43xx_write16(bcm, BCM43xx_MMIO_PS_STATUS, 0x0002);16171617+}16181618+16191619+static void bcm43xx_generate_beacon_template(struct bcm43xx_private *bcm,16201620+ u16 ram_offset, u16 shm_size_offset)16211621+{16221622+ u32 value;16231623+ u16 size = 0;16241624+16251625+ /* Timestamp. */16261626+ //FIXME: assumption: The chip sets the timestamp16271627+ value = 0;16281628+ bcm43xx_ram_write(bcm, ram_offset++, value);16291629+ bcm43xx_ram_write(bcm, ram_offset++, value);16301630+ size += 8;16311631+16321632+ /* Beacon Interval / Capability Information */16331633+ value = 0x0000;//FIXME: Which interval?16341634+ value |= (1 << 0) << 16; /* ESS */16351635+ value |= (1 << 2) << 16; /* CF Pollable */ //FIXME?16361636+ value |= (1 << 3) << 16; /* CF Poll Request */ //FIXME?16371637+ if (!bcm->ieee->open_wep)16381638+ value |= (1 << 4) << 16; /* Privacy */16391639+ bcm43xx_ram_write(bcm, ram_offset++, value);16401640+ size += 4;16411641+16421642+ /* SSID */16431643+ //TODO16441644+16451645+ /* FH Parameter Set */16461646+ //TODO16471647+16481648+ /* DS Parameter Set */16491649+ //TODO16501650+16511651+ /* CF Parameter Set */16521652+ //TODO16531653+16541654+ /* TIM */16551655+ //TODO16561656+16571657+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, shm_size_offset, size);16581658+}16591659+16601660+static void handle_irq_beacon(struct bcm43xx_private *bcm)16611661+{16621662+ u32 status;16631663+16641664+ bcm->irq_savedstate &= ~BCM43xx_IRQ_BEACON;16651665+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD);16661666+16671667+ if ((status & 0x1) && (status & 0x2)) {16681668+ /* ACK beacon IRQ. */16691669+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON,16701670+ BCM43xx_IRQ_BEACON);16711671+ bcm->irq_savedstate |= BCM43xx_IRQ_BEACON;16721672+ return;16731673+ }16741674+ if (!(status & 0x1)) {16751675+ bcm43xx_generate_beacon_template(bcm, 0x68, 0x18);16761676+ status |= 0x1;16771677+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, status);16781678+ }16791679+ if (!(status & 0x2)) {16801680+ bcm43xx_generate_beacon_template(bcm, 0x468, 0x1A);16811681+ status |= 0x2;16821682+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, status);16831683+ }16841684+}16851685+16861686+/* Interrupt handler bottom-half */16871687+static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm)16881688+{16891689+ u32 reason;16901690+ u32 dma_reason[4];16911691+ int activity = 0;16921692+ unsigned long flags;16931693+16941694+#ifdef CONFIG_BCM43XX_DEBUG16951695+ u32 _handled = 0x00000000;16961696+# define bcmirq_handled(irq) do { _handled |= (irq); } while (0)16971697+#else16981698+# define bcmirq_handled(irq) do { /* nothing */ } while (0)16991699+#endif /* CONFIG_BCM43XX_DEBUG*/17001700+17011701+ bcm43xx_lock_mmio(bcm, flags);17021702+ reason = bcm->irq_reason;17031703+ dma_reason[0] = bcm->dma_reason[0];17041704+ dma_reason[1] = bcm->dma_reason[1];17051705+ dma_reason[2] = bcm->dma_reason[2];17061706+ dma_reason[3] = bcm->dma_reason[3];17071707+17081708+ if (unlikely(reason & BCM43xx_IRQ_XMIT_ERROR)) {17091709+ /* TX error. We get this when Template Ram is written in wrong endianess17101710+ * in dummy_tx(). We also get this if something is wrong with the TX header17111711+ * on DMA or PIO queues.17121712+ * Maybe we get this in other error conditions, too.17131713+ */17141714+ printkl(KERN_ERR PFX "FATAL ERROR: BCM43xx_IRQ_XMIT_ERROR\n");17151715+ bcmirq_handled(BCM43xx_IRQ_XMIT_ERROR);17161716+ }17171717+ if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_FATALMASK) |17181718+ (dma_reason[1] & BCM43xx_DMAIRQ_FATALMASK) |17191719+ (dma_reason[2] & BCM43xx_DMAIRQ_FATALMASK) |17201720+ (dma_reason[3] & BCM43xx_DMAIRQ_FATALMASK))) {17211721+ printkl(KERN_ERR PFX "FATAL ERROR: Fatal DMA error: "17221722+ "0x%08X, 0x%08X, 0x%08X, 0x%08X\n",17231723+ dma_reason[0], dma_reason[1],17241724+ dma_reason[2], dma_reason[3]);17251725+ bcm43xx_controller_restart(bcm, "DMA error");17261726+ bcm43xx_unlock_mmio(bcm, flags);17271727+ return;17281728+ }17291729+ if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_NONFATALMASK) |17301730+ (dma_reason[1] & BCM43xx_DMAIRQ_NONFATALMASK) |17311731+ (dma_reason[2] & BCM43xx_DMAIRQ_NONFATALMASK) |17321732+ (dma_reason[3] & BCM43xx_DMAIRQ_NONFATALMASK))) {17331733+ printkl(KERN_ERR PFX "DMA error: "17341734+ "0x%08X, 0x%08X, 0x%08X, 0x%08X\n",17351735+ dma_reason[0], dma_reason[1],17361736+ dma_reason[2], dma_reason[3]);17371737+ }17381738+17391739+ if (reason & BCM43xx_IRQ_PS) {17401740+ handle_irq_ps(bcm);17411741+ bcmirq_handled(BCM43xx_IRQ_PS);17421742+ }17431743+17441744+ if (reason & BCM43xx_IRQ_REG124) {17451745+ handle_irq_reg124(bcm);17461746+ bcmirq_handled(BCM43xx_IRQ_REG124);17471747+ }17481748+17491749+ if (reason & BCM43xx_IRQ_BEACON) {17501750+ if (bcm->ieee->iw_mode == IW_MODE_MASTER)17511751+ handle_irq_beacon(bcm);17521752+ bcmirq_handled(BCM43xx_IRQ_BEACON);17531753+ }17541754+17551755+ if (reason & BCM43xx_IRQ_PMQ) {17561756+ handle_irq_pmq(bcm);17571757+ bcmirq_handled(BCM43xx_IRQ_PMQ);17581758+ }17591759+17601760+ if (reason & BCM43xx_IRQ_SCAN) {17611761+ /*TODO*/17621762+ //bcmirq_handled(BCM43xx_IRQ_SCAN);17631763+ }17641764+17651765+ if (reason & BCM43xx_IRQ_NOISE) {17661766+ handle_irq_noise(bcm);17671767+ bcmirq_handled(BCM43xx_IRQ_NOISE);17681768+ }17691769+17701770+ /* Check the DMA reason registers for received data. */17711771+ assert(!(dma_reason[1] & BCM43xx_DMAIRQ_RX_DONE));17721772+ assert(!(dma_reason[2] & BCM43xx_DMAIRQ_RX_DONE));17731773+ if (dma_reason[0] & BCM43xx_DMAIRQ_RX_DONE) {17741774+ if (bcm43xx_using_pio(bcm))17751775+ bcm43xx_pio_rx(bcm43xx_current_pio(bcm)->queue0);17761776+ else17771777+ bcm43xx_dma_rx(bcm43xx_current_dma(bcm)->rx_ring0);17781778+ /* We intentionally don't set "activity" to 1, here. */17791779+ }17801780+ if (dma_reason[3] & BCM43xx_DMAIRQ_RX_DONE) {17811781+ if (bcm43xx_using_pio(bcm))17821782+ bcm43xx_pio_rx(bcm43xx_current_pio(bcm)->queue3);17831783+ else17841784+ bcm43xx_dma_rx(bcm43xx_current_dma(bcm)->rx_ring1);17851785+ activity = 1;17861786+ }17871787+ bcmirq_handled(BCM43xx_IRQ_RX);17881788+17891789+ if (reason & BCM43xx_IRQ_XMIT_STATUS) {17901790+ handle_irq_transmit_status(bcm);17911791+ activity = 1;17921792+ //TODO: In AP mode, this also causes sending of powersave responses.17931793+ bcmirq_handled(BCM43xx_IRQ_XMIT_STATUS);17941794+ }17951795+17961796+ /* IRQ_PIO_WORKAROUND is handled in the top-half. */17971797+ bcmirq_handled(BCM43xx_IRQ_PIO_WORKAROUND);17981798+#ifdef CONFIG_BCM43XX_DEBUG17991799+ if (unlikely(reason & ~_handled)) {18001800+ printkl(KERN_WARNING PFX18011801+ "Unhandled IRQ! Reason: 0x%08x, Unhandled: 0x%08x, "18021802+ "DMA: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",18031803+ reason, (reason & ~_handled),18041804+ dma_reason[0], dma_reason[1],18051805+ dma_reason[2], dma_reason[3]);18061806+ }18071807+#endif18081808+#undef bcmirq_handled18091809+18101810+ if (!modparam_noleds)18111811+ bcm43xx_leds_update(bcm, activity);18121812+ bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate);18131813+ bcm43xx_unlock_mmio(bcm, flags);18141814+}18151815+18161816+static void pio_irq_workaround(struct bcm43xx_private *bcm,18171817+ u16 base, int queueidx)18181818+{18191819+ u16 rxctl;18201820+18211821+ rxctl = bcm43xx_read16(bcm, base + BCM43xx_PIO_RXCTL);18221822+ if (rxctl & BCM43xx_PIO_RXCTL_DATAAVAILABLE)18231823+ bcm->dma_reason[queueidx] |= BCM43xx_DMAIRQ_RX_DONE;18241824+ else18251825+ bcm->dma_reason[queueidx] &= ~BCM43xx_DMAIRQ_RX_DONE;18261826+}18271827+18281828+static void bcm43xx_interrupt_ack(struct bcm43xx_private *bcm, u32 reason)18291829+{18301830+ if (bcm43xx_using_pio(bcm) &&18311831+ (bcm->current_core->rev < 3) &&18321832+ (!(reason & BCM43xx_IRQ_PIO_WORKAROUND))) {18331833+ /* Apply a PIO specific workaround to the dma_reasons */18341834+ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO1_BASE, 0);18351835+ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO2_BASE, 1);18361836+ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO3_BASE, 2);18371837+ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO4_BASE, 3);18381838+ }18391839+18401840+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, reason);18411841+18421842+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_REASON,18431843+ bcm->dma_reason[0]);18441844+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA2_REASON,18451845+ bcm->dma_reason[1]);18461846+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_REASON,18471847+ bcm->dma_reason[2]);18481848+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_REASON,18491849+ bcm->dma_reason[3]);18501850+}18511851+18521852+/* Interrupt handler top-half */18531853+static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_regs *regs)18541854+{18551855+ irqreturn_t ret = IRQ_HANDLED;18561856+ struct bcm43xx_private *bcm = dev_id;18571857+ u32 reason;18581858+18591859+ if (!bcm)18601860+ return IRQ_NONE;18611861+18621862+ spin_lock(&bcm->_lock);18631863+18641864+ reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);18651865+ if (reason == 0xffffffff) {18661866+ /* irq not for us (shared irq) */18671867+ ret = IRQ_NONE;18681868+ goto out;18691869+ }18701870+ reason &= bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK);18711871+ if (!reason)18721872+ goto out;18731873+18741874+ bcm->dma_reason[0] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA1_REASON)18751875+ & 0x0001dc00;18761876+ bcm->dma_reason[1] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA2_REASON)18771877+ & 0x0000dc00;18781878+ bcm->dma_reason[2] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA3_REASON)18791879+ & 0x0000dc00;18801880+ bcm->dma_reason[3] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA4_REASON)18811881+ & 0x0001dc00;18821882+18831883+ bcm43xx_interrupt_ack(bcm, reason);18841884+18851885+ /* Only accept IRQs, if we are initialized properly.18861886+ * This avoids an RX race while initializing.18871887+ * We should probably not enable IRQs before we are initialized18881888+ * completely, but some careful work is needed to fix this. I think it18891889+ * is best to stay with this cheap workaround for now... .18901890+ */18911891+ if (likely(bcm->initialized)) {18921892+ /* disable all IRQs. They are enabled again in the bottom half. */18931893+ bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);18941894+ /* save the reason code and call our bottom half. */18951895+ bcm->irq_reason = reason;18961896+ tasklet_schedule(&bcm->isr_tasklet);18971897+ }18981898+18991899+out:19001900+ mmiowb();19011901+ spin_unlock(&bcm->_lock);19021902+19031903+ return ret;19041904+}19051905+19061906+static void bcm43xx_release_firmware(struct bcm43xx_private *bcm, int force)19071907+{19081908+ if (bcm->firmware_norelease && !force)19091909+ return; /* Suspending or controller reset. */19101910+ release_firmware(bcm->ucode);19111911+ bcm->ucode = NULL;19121912+ release_firmware(bcm->pcm);19131913+ bcm->pcm = NULL;19141914+ release_firmware(bcm->initvals0);19151915+ bcm->initvals0 = NULL;19161916+ release_firmware(bcm->initvals1);19171917+ bcm->initvals1 = NULL;19181918+}19191919+19201920+static int bcm43xx_request_firmware(struct bcm43xx_private *bcm)19211921+{19221922+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);19231923+ u8 rev = bcm->current_core->rev;19241924+ int err = 0;19251925+ int nr;19261926+ char buf[22 + sizeof(modparam_fwpostfix) - 1] = { 0 };19271927+19281928+ if (!bcm->ucode) {19291929+ snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_microcode%d%s.fw",19301930+ (rev >= 5 ? 5 : rev),19311931+ modparam_fwpostfix);19321932+ err = request_firmware(&bcm->ucode, buf, &bcm->pci_dev->dev);19331933+ if (err) {19341934+ printk(KERN_ERR PFX 19351935+ "Error: Microcode \"%s\" not available or load failed.\n",19361936+ buf);19371937+ goto error;19381938+ }19391939+ }19401940+19411941+ if (!bcm->pcm) {19421942+ snprintf(buf, ARRAY_SIZE(buf),19431943+ "bcm43xx_pcm%d%s.fw",19441944+ (rev < 5 ? 4 : 5),19451945+ modparam_fwpostfix);19461946+ err = request_firmware(&bcm->pcm, buf, &bcm->pci_dev->dev);19471947+ if (err) {19481948+ printk(KERN_ERR PFX19491949+ "Error: PCM \"%s\" not available or load failed.\n",19501950+ buf);19511951+ goto error;19521952+ }19531953+ }19541954+19551955+ if (!bcm->initvals0) {19561956+ if (rev == 2 || rev == 4) {19571957+ switch (phy->type) {19581958+ case BCM43xx_PHYTYPE_A:19591959+ nr = 3;19601960+ break;19611961+ case BCM43xx_PHYTYPE_B:19621962+ case BCM43xx_PHYTYPE_G:19631963+ nr = 1;19641964+ break;19651965+ default:19661966+ goto err_noinitval;19671967+ }19681968+19691969+ } else if (rev >= 5) {19701970+ switch (phy->type) {19711971+ case BCM43xx_PHYTYPE_A:19721972+ nr = 7;19731973+ break;19741974+ case BCM43xx_PHYTYPE_B:19751975+ case BCM43xx_PHYTYPE_G:19761976+ nr = 5;19771977+ break;19781978+ default:19791979+ goto err_noinitval;19801980+ }19811981+ } else19821982+ goto err_noinitval;19831983+ snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw",19841984+ nr, modparam_fwpostfix);19851985+19861986+ err = request_firmware(&bcm->initvals0, buf, &bcm->pci_dev->dev);19871987+ if (err) {19881988+ printk(KERN_ERR PFX 19891989+ "Error: InitVals \"%s\" not available or load failed.\n",19901990+ buf);19911991+ goto error;19921992+ }19931993+ if (bcm->initvals0->size % sizeof(struct bcm43xx_initval)) {19941994+ printk(KERN_ERR PFX "InitVals fileformat error.\n");19951995+ goto error;19961996+ }19971997+ }19981998+19991999+ if (!bcm->initvals1) {20002000+ if (rev >= 5) {20012001+ u32 sbtmstatehigh;20022002+20032003+ switch (phy->type) {20042004+ case BCM43xx_PHYTYPE_A:20052005+ sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH);20062006+ if (sbtmstatehigh & 0x00010000)20072007+ nr = 9;20082008+ else20092009+ nr = 10;20102010+ break;20112011+ case BCM43xx_PHYTYPE_B:20122012+ case BCM43xx_PHYTYPE_G:20132013+ nr = 6;20142014+ break;20152015+ default:20162016+ goto err_noinitval;20172017+ }20182018+ snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw",20192019+ nr, modparam_fwpostfix);20202020+20212021+ err = request_firmware(&bcm->initvals1, buf, &bcm->pci_dev->dev);20222022+ if (err) {20232023+ printk(KERN_ERR PFX 20242024+ "Error: InitVals \"%s\" not available or load failed.\n",20252025+ buf);20262026+ goto error;20272027+ }20282028+ if (bcm->initvals1->size % sizeof(struct bcm43xx_initval)) {20292029+ printk(KERN_ERR PFX "InitVals fileformat error.\n");20302030+ goto error;20312031+ }20322032+ }20332033+ }20342034+20352035+out:20362036+ return err;20372037+error:20382038+ bcm43xx_release_firmware(bcm, 1);20392039+ goto out;20402040+err_noinitval:20412041+ printk(KERN_ERR PFX "Error: No InitVals available!\n");20422042+ err = -ENOENT;20432043+ goto error;20442044+}20452045+20462046+static void bcm43xx_upload_microcode(struct bcm43xx_private *bcm)20472047+{20482048+ const u32 *data;20492049+ unsigned int i, len;20502050+20512051+ /* Upload Microcode. */20522052+ data = (u32 *)(bcm->ucode->data);20532053+ len = bcm->ucode->size / sizeof(u32);20542054+ bcm43xx_shm_control_word(bcm, BCM43xx_SHM_UCODE, 0x0000);20552055+ for (i = 0; i < len; i++) {20562056+ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA,20572057+ be32_to_cpu(data[i]));20582058+ udelay(10);20592059+ }20602060+20612061+ /* Upload PCM data. */20622062+ data = (u32 *)(bcm->pcm->data);20632063+ len = bcm->pcm->size / sizeof(u32);20642064+ bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01ea);20652065+ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, 0x00004000);20662066+ bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01eb);20672067+ for (i = 0; i < len; i++) {20682068+ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA,20692069+ be32_to_cpu(data[i]));20702070+ udelay(10);20712071+ }20722072+}20732073+20742074+static int bcm43xx_write_initvals(struct bcm43xx_private *bcm,20752075+ const struct bcm43xx_initval *data,20762076+ const unsigned int len)20772077+{20782078+ u16 offset, size;20792079+ u32 value;20802080+ unsigned int i;20812081+20822082+ for (i = 0; i < len; i++) {20832083+ offset = be16_to_cpu(data[i].offset);20842084+ size = be16_to_cpu(data[i].size);20852085+ value = be32_to_cpu(data[i].value);20862086+20872087+ if (unlikely(offset >= 0x1000))20882088+ goto err_format;20892089+ if (size == 2) {20902090+ if (unlikely(value & 0xFFFF0000))20912091+ goto err_format;20922092+ bcm43xx_write16(bcm, offset, (u16)value);20932093+ } else if (size == 4) {20942094+ bcm43xx_write32(bcm, offset, value);20952095+ } else20962096+ goto err_format;20972097+ }20982098+20992099+ return 0;21002100+21012101+err_format:21022102+ printk(KERN_ERR PFX "InitVals (bcm43xx_initvalXX.fw) file-format error. "21032103+ "Please fix your bcm43xx firmware files.\n");21042104+ return -EPROTO;21052105+}21062106+21072107+static int bcm43xx_upload_initvals(struct bcm43xx_private *bcm)21082108+{21092109+ int err;21102110+21112111+ err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals0->data,21122112+ bcm->initvals0->size / sizeof(struct bcm43xx_initval));21132113+ if (err)21142114+ goto out;21152115+ if (bcm->initvals1) {21162116+ err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals1->data,21172117+ bcm->initvals1->size / sizeof(struct bcm43xx_initval));21182118+ if (err)21192119+ goto out;21202120+ }21212121+out:21222122+ return err;21232123+}21242124+21252125+static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm)21262126+{21272127+ int res;21282128+ unsigned int i;21292129+ u32 data;21302130+21312131+ bcm->irq = bcm->pci_dev->irq;21322132+#ifdef CONFIG_BCM947XX21332133+ if (bcm->pci_dev->bus->number == 0) {21342134+ struct pci_dev *d = NULL;21352135+ /* FIXME: we will probably need more device IDs here... */21362136+ d = pci_find_device(PCI_VENDOR_ID_BROADCOM, 0x4324, NULL);21372137+ if (d != NULL) {21382138+ bcm->irq = d->irq;21392139+ }21402140+ }21412141+#endif21422142+ res = request_irq(bcm->irq, bcm43xx_interrupt_handler,21432143+ SA_SHIRQ, KBUILD_MODNAME, bcm);21442144+ if (res) {21452145+ printk(KERN_ERR PFX "Cannot register IRQ%d\n", bcm->irq);21462146+ return -ENODEV;21472147+ }21482148+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xffffffff);21492149+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402);21502150+ i = 0;21512151+ while (1) {21522152+ data = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);21532153+ if (data == BCM43xx_IRQ_READY)21542154+ break;21552155+ i++;21562156+ if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) {21572157+ printk(KERN_ERR PFX "Card IRQ register not responding. "21582158+ "Giving up.\n");21592159+ free_irq(bcm->irq, bcm);21602160+ return -ENODEV;21612161+ }21622162+ udelay(10);21632163+ }21642164+ // dummy read21652165+ bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);21662166+21672167+ return 0;21682168+}21692169+21702170+/* Switch to the core used to write the GPIO register.21712171+ * This is either the ChipCommon, or the PCI core.21722172+ */21732173+static int switch_to_gpio_core(struct bcm43xx_private *bcm)21742174+{21752175+ int err;21762176+21772177+ /* Where to find the GPIO register depends on the chipset.21782178+ * If it has a ChipCommon, its register at offset 0x6c is the GPIO21792179+ * control register. Otherwise the register at offset 0x6c in the21802180+ * PCI core is the GPIO control register.21812181+ */21822182+ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);21832183+ if (err == -ENODEV) {21842184+ err = bcm43xx_switch_core(bcm, &bcm->core_pci);21852185+ if (unlikely(err == -ENODEV)) {21862186+ printk(KERN_ERR PFX "gpio error: "21872187+ "Neither ChipCommon nor PCI core available!\n");21882188+ }21892189+ }21902190+21912191+ return err;21922192+}21932193+21942194+/* Initialize the GPIOs21952195+ * http://bcm-specs.sipsolutions.net/GPIO21962196+ */21972197+static int bcm43xx_gpio_init(struct bcm43xx_private *bcm)21982198+{21992199+ struct bcm43xx_coreinfo *old_core;22002200+ int err;22012201+ u32 mask, set;22022202+22032203+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,22042204+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)22052205+ & 0xFFFF3FFF);22062206+22072207+ bcm43xx_leds_switch_all(bcm, 0);22082208+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK,22092209+ bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK) | 0x000F);22102210+22112211+ mask = 0x0000001F;22122212+ set = 0x0000000F;22132213+ if (bcm->chip_id == 0x4301) {22142214+ mask |= 0x0060;22152215+ set |= 0x0060;22162216+ }22172217+ if (0 /* FIXME: conditional unknown */) {22182218+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK,22192219+ bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK)22202220+ | 0x0100);22212221+ mask |= 0x0180;22222222+ set |= 0x0180;22232223+ }22242224+ if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) {22252225+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK,22262226+ bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK)22272227+ | 0x0200);22282228+ mask |= 0x0200;22292229+ set |= 0x0200;22302230+ }22312231+ if (bcm->current_core->rev >= 2)22322232+ mask |= 0x0010; /* FIXME: This is redundant. */22332233+22342234+ old_core = bcm->current_core;22352235+ err = switch_to_gpio_core(bcm);22362236+ if (err)22372237+ goto out;22382238+ bcm43xx_write32(bcm, BCM43xx_GPIO_CONTROL,22392239+ (bcm43xx_read32(bcm, BCM43xx_GPIO_CONTROL) & mask) | set);22402240+ err = bcm43xx_switch_core(bcm, old_core);22412241+out:22422242+ return err;22432243+}22442244+22452245+/* Turn off all GPIO stuff. Call this on module unload, for example. */22462246+static int bcm43xx_gpio_cleanup(struct bcm43xx_private *bcm)22472247+{22482248+ struct bcm43xx_coreinfo *old_core;22492249+ int err;22502250+22512251+ old_core = bcm->current_core;22522252+ err = switch_to_gpio_core(bcm);22532253+ if (err)22542254+ return err;22552255+ bcm43xx_write32(bcm, BCM43xx_GPIO_CONTROL, 0x00000000);22562256+ err = bcm43xx_switch_core(bcm, old_core);22572257+ assert(err == 0);22582258+22592259+ return 0;22602260+}22612261+22622262+/* http://bcm-specs.sipsolutions.net/EnableMac */22632263+void bcm43xx_mac_enable(struct bcm43xx_private *bcm)22642264+{22652265+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,22662266+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)22672267+ | BCM43xx_SBF_MAC_ENABLED);22682268+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, BCM43xx_IRQ_READY);22692269+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */22702270+ bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */22712271+ bcm43xx_power_saving_ctl_bits(bcm, -1, -1);22722272+}22732273+22742274+/* http://bcm-specs.sipsolutions.net/SuspendMAC */22752275+void bcm43xx_mac_suspend(struct bcm43xx_private *bcm)22762276+{22772277+ int i;22782278+ u32 tmp;22792279+22802280+ bcm43xx_power_saving_ctl_bits(bcm, -1, 1);22812281+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,22822282+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)22832283+ & ~BCM43xx_SBF_MAC_ENABLED);22842284+ bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */22852285+ for (i = 100000; i; i--) {22862286+ tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);22872287+ if (tmp & BCM43xx_IRQ_READY)22882288+ return;22892289+ udelay(10);22902290+ }22912291+ printkl(KERN_ERR PFX "MAC suspend failed\n");22922292+}22932293+22942294+void bcm43xx_set_iwmode(struct bcm43xx_private *bcm,22952295+ int iw_mode)22962296+{22972297+ unsigned long flags;22982298+ struct net_device *net_dev = bcm->net_dev;22992299+ u32 status;23002300+ u16 value;23012301+23022302+ spin_lock_irqsave(&bcm->ieee->lock, flags);23032303+ bcm->ieee->iw_mode = iw_mode;23042304+ spin_unlock_irqrestore(&bcm->ieee->lock, flags);23052305+ if (iw_mode == IW_MODE_MONITOR)23062306+ net_dev->type = ARPHRD_IEEE80211;23072307+ else23082308+ net_dev->type = ARPHRD_ETHER;23092309+23102310+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);23112311+ /* Reset status to infrastructured mode */23122312+ status &= ~(BCM43xx_SBF_MODE_AP | BCM43xx_SBF_MODE_MONITOR);23132313+ status &= ~BCM43xx_SBF_MODE_PROMISC;23142314+ status |= BCM43xx_SBF_MODE_NOTADHOC;23152315+23162316+/* FIXME: Always enable promisc mode, until we get the MAC filters working correctly. */23172317+status |= BCM43xx_SBF_MODE_PROMISC;23182318+23192319+ switch (iw_mode) {23202320+ case IW_MODE_MONITOR:23212321+ status |= BCM43xx_SBF_MODE_MONITOR;23222322+ status |= BCM43xx_SBF_MODE_PROMISC;23232323+ break;23242324+ case IW_MODE_ADHOC:23252325+ status &= ~BCM43xx_SBF_MODE_NOTADHOC;23262326+ break;23272327+ case IW_MODE_MASTER:23282328+ status |= BCM43xx_SBF_MODE_AP;23292329+ break;23302330+ case IW_MODE_SECOND:23312331+ case IW_MODE_REPEAT:23322332+ TODO(); /* TODO */23332333+ break;23342334+ case IW_MODE_INFRA:23352335+ /* nothing to be done here... */23362336+ break;23372337+ default:23382338+ dprintk(KERN_ERR PFX "Unknown mode in set_iwmode: %d\n", iw_mode);23392339+ }23402340+ if (net_dev->flags & IFF_PROMISC)23412341+ status |= BCM43xx_SBF_MODE_PROMISC;23422342+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);23432343+23442344+ value = 0x0002;23452345+ if (iw_mode != IW_MODE_ADHOC && iw_mode != IW_MODE_MASTER) {23462346+ if (bcm->chip_id == 0x4306 && bcm->chip_rev == 3)23472347+ value = 0x0064;23482348+ else23492349+ value = 0x0032;23502350+ }23512351+ bcm43xx_write16(bcm, 0x0612, value);23522352+}23532353+23542354+/* This is the opposite of bcm43xx_chip_init() */23552355+static void bcm43xx_chip_cleanup(struct bcm43xx_private *bcm)23562356+{23572357+ bcm43xx_radio_turn_off(bcm);23582358+ if (!modparam_noleds)23592359+ bcm43xx_leds_exit(bcm);23602360+ bcm43xx_gpio_cleanup(bcm);23612361+ free_irq(bcm->irq, bcm);23622362+ bcm43xx_release_firmware(bcm, 0);23632363+}23642364+23652365+/* Initialize the chip23662366+ * http://bcm-specs.sipsolutions.net/ChipInit23672367+ */23682368+static int bcm43xx_chip_init(struct bcm43xx_private *bcm)23692369+{23702370+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);23712371+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);23722372+ int err;23732373+ int tmp;23742374+ u32 value32;23752375+ u16 value16;23762376+23772377+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,23782378+ BCM43xx_SBF_CORE_READY23792379+ | BCM43xx_SBF_400);23802380+23812381+ err = bcm43xx_request_firmware(bcm);23822382+ if (err)23832383+ goto out;23842384+ bcm43xx_upload_microcode(bcm);23852385+23862386+ err = bcm43xx_initialize_irq(bcm);23872387+ if (err)23882388+ goto err_release_fw;23892389+23902390+ err = bcm43xx_gpio_init(bcm);23912391+ if (err)23922392+ goto err_free_irq;23932393+23942394+ err = bcm43xx_upload_initvals(bcm);23952395+ if (err)23962396+ goto err_gpio_cleanup;23972397+ bcm43xx_radio_turn_on(bcm);23982398+23992399+ bcm43xx_write16(bcm, 0x03E6, 0x0000);24002400+ err = bcm43xx_phy_init(bcm);24012401+ if (err)24022402+ goto err_radio_off;24032403+24042404+ /* Select initial Interference Mitigation. */24052405+ tmp = radio->interfmode;24062406+ radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE;24072407+ bcm43xx_radio_set_interference_mitigation(bcm, tmp);24082408+24092409+ bcm43xx_phy_set_antenna_diversity(bcm);24102410+ bcm43xx_radio_set_txantenna(bcm, BCM43xx_RADIO_TXANTENNA_DEFAULT);24112411+ if (phy->type == BCM43xx_PHYTYPE_B) {24122412+ value16 = bcm43xx_read16(bcm, 0x005E);24132413+ value16 |= 0x0004;24142414+ bcm43xx_write16(bcm, 0x005E, value16);24152415+ }24162416+ bcm43xx_write32(bcm, 0x0100, 0x01000000);24172417+ if (bcm->current_core->rev < 5)24182418+ bcm43xx_write32(bcm, 0x010C, 0x01000000);24192419+24202420+ value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);24212421+ value32 &= ~ BCM43xx_SBF_MODE_NOTADHOC;24222422+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32);24232423+ value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);24242424+ value32 |= BCM43xx_SBF_MODE_NOTADHOC;24252425+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32);24262426+24272427+ value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);24282428+ value32 |= 0x100000;24292429+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32);24302430+24312431+ if (bcm43xx_using_pio(bcm)) {24322432+ bcm43xx_write32(bcm, 0x0210, 0x00000100);24332433+ bcm43xx_write32(bcm, 0x0230, 0x00000100);24342434+ bcm43xx_write32(bcm, 0x0250, 0x00000100);24352435+ bcm43xx_write32(bcm, 0x0270, 0x00000100);24362436+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0034, 0x0000);24372437+ }24382438+24392439+ /* Probe Response Timeout value */24402440+ /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */24412441+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0074, 0x0000);24422442+24432443+ /* Initially set the wireless operation mode. */24442444+ bcm43xx_set_iwmode(bcm, bcm->ieee->iw_mode);24452445+24462446+ if (bcm->current_core->rev < 3) {24472447+ bcm43xx_write16(bcm, 0x060E, 0x0000);24482448+ bcm43xx_write16(bcm, 0x0610, 0x8000);24492449+ bcm43xx_write16(bcm, 0x0604, 0x0000);24502450+ bcm43xx_write16(bcm, 0x0606, 0x0200);24512451+ } else {24522452+ bcm43xx_write32(bcm, 0x0188, 0x80000000);24532453+ bcm43xx_write32(bcm, 0x018C, 0x02000000);24542454+ }24552455+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0x00004000);24562456+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_IRQ_MASK, 0x0001DC00);24572457+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA2_IRQ_MASK, 0x0000DC00);24582458+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_IRQ_MASK, 0x0000DC00);24592459+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_IRQ_MASK, 0x0001DC00);24602460+24612461+ value32 = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);24622462+ value32 |= 0x00100000;24632463+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, value32);24642464+24652465+ bcm43xx_write16(bcm, BCM43xx_MMIO_POWERUP_DELAY, bcm43xx_pctl_powerup_delay(bcm));24662466+24672467+ assert(err == 0);24682468+ dprintk(KERN_INFO PFX "Chip initialized\n");24692469+out:24702470+ return err;24712471+24722472+err_radio_off:24732473+ bcm43xx_radio_turn_off(bcm);24742474+err_gpio_cleanup:24752475+ bcm43xx_gpio_cleanup(bcm);24762476+err_free_irq:24772477+ free_irq(bcm->irq, bcm);24782478+err_release_fw:24792479+ bcm43xx_release_firmware(bcm, 1);24802480+ goto out;24812481+}24822482+24832483+/* Validate chip access24842484+ * http://bcm-specs.sipsolutions.net/ValidateChipAccess */24852485+static int bcm43xx_validate_chip(struct bcm43xx_private *bcm)24862486+{24872487+ u32 value;24882488+ u32 shm_backup;24892489+24902490+ shm_backup = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000);24912491+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0xAA5555AA);24922492+ if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0xAA5555AA)24932493+ goto error;24942494+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0x55AAAA55);24952495+ if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0x55AAAA55)24962496+ goto error;24972497+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, shm_backup);24982498+24992499+ value = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);25002500+ if ((value | 0x80000000) != 0x80000400)25012501+ goto error;25022502+25032503+ value = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);25042504+ if (value != 0x00000000)25052505+ goto error;25062506+25072507+ return 0;25082508+error:25092509+ printk(KERN_ERR PFX "Failed to validate the chipaccess\n");25102510+ return -ENODEV;25112511+}25122512+25132513+static void bcm43xx_init_struct_phyinfo(struct bcm43xx_phyinfo *phy)25142514+{25152515+ /* Initialize a "phyinfo" structure. The structure is already25162516+ * zeroed out.25172517+ */25182518+ phy->antenna_diversity = 0xFFFF;25192519+ phy->savedpctlreg = 0xFFFF;25202520+ phy->minlowsig[0] = 0xFFFF;25212521+ phy->minlowsig[1] = 0xFFFF;25222522+ spin_lock_init(&phy->lock);25232523+}25242524+25252525+static void bcm43xx_init_struct_radioinfo(struct bcm43xx_radioinfo *radio)25262526+{25272527+ /* Initialize a "radioinfo" structure. The structure is already25282528+ * zeroed out.25292529+ */25302530+ radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE;25312531+ radio->channel = 0xFF;25322532+ radio->initial_channel = 0xFF;25332533+ radio->lofcal = 0xFFFF;25342534+ radio->initval = 0xFFFF;25352535+ radio->nrssi[0] = -1000;25362536+ radio->nrssi[1] = -1000;25372537+}25382538+25392539+static int bcm43xx_probe_cores(struct bcm43xx_private *bcm)25402540+{25412541+ int err, i;25422542+ int current_core;25432543+ u32 core_vendor, core_id, core_rev;25442544+ u32 sb_id_hi, chip_id_32 = 0;25452545+ u16 pci_device, chip_id_16;25462546+ u8 core_count;25472547+25482548+ memset(&bcm->core_chipcommon, 0, sizeof(struct bcm43xx_coreinfo));25492549+ memset(&bcm->core_pci, 0, sizeof(struct bcm43xx_coreinfo));25502550+ memset(&bcm->core_80211, 0, sizeof(struct bcm43xx_coreinfo)25512551+ * BCM43xx_MAX_80211_CORES);25522552+ memset(&bcm->core_80211_ext, 0, sizeof(struct bcm43xx_coreinfo_80211)25532553+ * BCM43xx_MAX_80211_CORES);25542554+ bcm->current_80211_core_idx = -1;25552555+ bcm->nr_80211_available = 0;25562556+ bcm->current_core = NULL;25572557+ bcm->active_80211_core = NULL;25582558+25592559+ /* map core 0 */25602560+ err = _switch_core(bcm, 0);25612561+ if (err)25622562+ goto out;25632563+25642564+ /* fetch sb_id_hi from core information registers */25652565+ sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI);25662566+25672567+ core_id = (sb_id_hi & 0xFFF0) >> 4;25682568+ core_rev = (sb_id_hi & 0xF);25692569+ core_vendor = (sb_id_hi & 0xFFFF0000) >> 16;25702570+25712571+ /* if present, chipcommon is always core 0; read the chipid from it */25722572+ if (core_id == BCM43xx_COREID_CHIPCOMMON) {25732573+ chip_id_32 = bcm43xx_read32(bcm, 0);25742574+ chip_id_16 = chip_id_32 & 0xFFFF;25752575+ bcm->core_chipcommon.available = 1;25762576+ bcm->core_chipcommon.id = core_id;25772577+ bcm->core_chipcommon.rev = core_rev;25782578+ bcm->core_chipcommon.index = 0;25792579+ /* While we are at it, also read the capabilities. */25802580+ bcm->chipcommon_capabilities = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_CAPABILITIES);25812581+ } else {25822582+ /* without a chipCommon, use a hard coded table. */25832583+ pci_device = bcm->pci_dev->device;25842584+ if (pci_device == 0x4301)25852585+ chip_id_16 = 0x4301;25862586+ else if ((pci_device >= 0x4305) && (pci_device <= 0x4307))25872587+ chip_id_16 = 0x4307;25882588+ else if ((pci_device >= 0x4402) && (pci_device <= 0x4403))25892589+ chip_id_16 = 0x4402;25902590+ else if ((pci_device >= 0x4610) && (pci_device <= 0x4615))25912591+ chip_id_16 = 0x4610;25922592+ else if ((pci_device >= 0x4710) && (pci_device <= 0x4715))25932593+ chip_id_16 = 0x4710;25942594+#ifdef CONFIG_BCM947XX25952595+ else if ((pci_device >= 0x4320) && (pci_device <= 0x4325))25962596+ chip_id_16 = 0x4309;25972597+#endif25982598+ else {25992599+ printk(KERN_ERR PFX "Could not determine Chip ID\n");26002600+ return -ENODEV;26012601+ }26022602+ }26032603+26042604+ /* ChipCommon with Core Rev >=4 encodes number of cores,26052605+ * otherwise consult hardcoded table */26062606+ if ((core_id == BCM43xx_COREID_CHIPCOMMON) && (core_rev >= 4)) {26072607+ core_count = (chip_id_32 & 0x0F000000) >> 24;26082608+ } else {26092609+ switch (chip_id_16) {26102610+ case 0x4610:26112611+ case 0x4704:26122612+ case 0x4710:26132613+ core_count = 9;26142614+ break;26152615+ case 0x4310:26162616+ core_count = 8;26172617+ break;26182618+ case 0x5365:26192619+ core_count = 7;26202620+ break;26212621+ case 0x4306:26222622+ core_count = 6;26232623+ break;26242624+ case 0x4301:26252625+ case 0x4307:26262626+ core_count = 5;26272627+ break;26282628+ case 0x4402:26292629+ core_count = 3;26302630+ break;26312631+ default:26322632+ /* SOL if we get here */26332633+ assert(0);26342634+ core_count = 1;26352635+ }26362636+ }26372637+26382638+ bcm->chip_id = chip_id_16;26392639+ bcm->chip_rev = (chip_id_32 & 0x000F0000) >> 16;26402640+ bcm->chip_package = (chip_id_32 & 0x00F00000) >> 20;26412641+26422642+ dprintk(KERN_INFO PFX "Chip ID 0x%x, rev 0x%x\n",26432643+ bcm->chip_id, bcm->chip_rev);26442644+ dprintk(KERN_INFO PFX "Number of cores: %d\n", core_count);26452645+ if (bcm->core_chipcommon.available) {26462646+ dprintk(KERN_INFO PFX "Core 0: ID 0x%x, rev 0x%x, vendor 0x%x, %s\n",26472647+ core_id, core_rev, core_vendor,26482648+ bcm43xx_core_enabled(bcm) ? "enabled" : "disabled");26492649+ }26502650+26512651+ if (bcm->core_chipcommon.available)26522652+ current_core = 1;26532653+ else26542654+ current_core = 0;26552655+ for ( ; current_core < core_count; current_core++) {26562656+ struct bcm43xx_coreinfo *core;26572657+ struct bcm43xx_coreinfo_80211 *ext_80211;26582658+26592659+ err = _switch_core(bcm, current_core);26602660+ if (err)26612661+ goto out;26622662+ /* Gather information */26632663+ /* fetch sb_id_hi from core information registers */26642664+ sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI);26652665+26662666+ /* extract core_id, core_rev, core_vendor */26672667+ core_id = (sb_id_hi & 0xFFF0) >> 4;26682668+ core_rev = (sb_id_hi & 0xF);26692669+ core_vendor = (sb_id_hi & 0xFFFF0000) >> 16;26702670+26712671+ dprintk(KERN_INFO PFX "Core %d: ID 0x%x, rev 0x%x, vendor 0x%x, %s\n",26722672+ current_core, core_id, core_rev, core_vendor,26732673+ bcm43xx_core_enabled(bcm) ? "enabled" : "disabled" );26742674+26752675+ core = NULL;26762676+ switch (core_id) {26772677+ case BCM43xx_COREID_PCI:26782678+ core = &bcm->core_pci;26792679+ if (core->available) {26802680+ printk(KERN_WARNING PFX "Multiple PCI cores found.\n");26812681+ continue;26822682+ }26832683+ break;26842684+ case BCM43xx_COREID_80211:26852685+ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {26862686+ core = &(bcm->core_80211[i]);26872687+ ext_80211 = &(bcm->core_80211_ext[i]);26882688+ if (!core->available)26892689+ break;26902690+ core = NULL;26912691+ }26922692+ if (!core) {26932693+ printk(KERN_WARNING PFX "More than %d cores of type 802.11 found.\n",26942694+ BCM43xx_MAX_80211_CORES);26952695+ continue;26962696+ }26972697+ if (i != 0) {26982698+ /* More than one 80211 core is only supported26992699+ * by special chips.27002700+ * There are chips with two 80211 cores, but with27012701+ * dangling pins on the second core. Be careful27022702+ * and ignore these cores here.27032703+ */27042704+ if (bcm->pci_dev->device != 0x4324) {27052705+ dprintk(KERN_INFO PFX "Ignoring additional 802.11 core.\n");27062706+ continue;27072707+ }27082708+ }27092709+ switch (core_rev) {27102710+ case 2:27112711+ case 4:27122712+ case 5:27132713+ case 6:27142714+ case 7:27152715+ case 9:27162716+ break;27172717+ default:27182718+ printk(KERN_ERR PFX "Error: Unsupported 80211 core revision %u\n",27192719+ core_rev);27202720+ err = -ENODEV;27212721+ goto out;27222722+ }27232723+ bcm->nr_80211_available++;27242724+ bcm43xx_init_struct_phyinfo(&ext_80211->phy);27252725+ bcm43xx_init_struct_radioinfo(&ext_80211->radio);27262726+ break;27272727+ case BCM43xx_COREID_CHIPCOMMON:27282728+ printk(KERN_WARNING PFX "Multiple CHIPCOMMON cores found.\n");27292729+ break;27302730+ }27312731+ if (core) {27322732+ core->available = 1;27332733+ core->id = core_id;27342734+ core->rev = core_rev;27352735+ core->index = current_core;27362736+ }27372737+ }27382738+27392739+ if (!bcm->core_80211[0].available) {27402740+ printk(KERN_ERR PFX "Error: No 80211 core found!\n");27412741+ err = -ENODEV;27422742+ goto out;27432743+ }27442744+27452745+ err = bcm43xx_switch_core(bcm, &bcm->core_80211[0]);27462746+27472747+ assert(err == 0);27482748+out:27492749+ return err;27502750+}27512751+27522752+static void bcm43xx_gen_bssid(struct bcm43xx_private *bcm)27532753+{27542754+ const u8 *mac = (const u8*)(bcm->net_dev->dev_addr);27552755+ u8 *bssid = bcm->ieee->bssid;27562756+27572757+ switch (bcm->ieee->iw_mode) {27582758+ case IW_MODE_ADHOC:27592759+ random_ether_addr(bssid);27602760+ break;27612761+ case IW_MODE_MASTER:27622762+ case IW_MODE_INFRA:27632763+ case IW_MODE_REPEAT:27642764+ case IW_MODE_SECOND:27652765+ case IW_MODE_MONITOR:27662766+ memcpy(bssid, mac, ETH_ALEN);27672767+ break;27682768+ default:27692769+ assert(0);27702770+ }27712771+}27722772+27732773+static void bcm43xx_rate_memory_write(struct bcm43xx_private *bcm,27742774+ u16 rate,27752775+ int is_ofdm)27762776+{27772777+ u16 offset;27782778+27792779+ if (is_ofdm) {27802780+ offset = 0x480;27812781+ offset += (bcm43xx_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2;27822782+ }27832783+ else {27842784+ offset = 0x4C0;27852785+ offset += (bcm43xx_plcp_get_ratecode_cck(rate) & 0x000F) * 2;27862786+ }27872787+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, offset + 0x20,27882788+ bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, offset));27892789+}27902790+27912791+static void bcm43xx_rate_memory_init(struct bcm43xx_private *bcm)27922792+{27932793+ switch (bcm43xx_current_phy(bcm)->type) {27942794+ case BCM43xx_PHYTYPE_A:27952795+ case BCM43xx_PHYTYPE_G:27962796+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_6MB, 1);27972797+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_12MB, 1);27982798+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_18MB, 1);27992799+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_24MB, 1);28002800+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_36MB, 1);28012801+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_48MB, 1);28022802+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_54MB, 1);28032803+ case BCM43xx_PHYTYPE_B:28042804+ bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_1MB, 0);28052805+ bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_2MB, 0);28062806+ bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_5MB, 0);28072807+ bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_11MB, 0);28082808+ break;28092809+ default:28102810+ assert(0);28112811+ }28122812+}28132813+28142814+static void bcm43xx_wireless_core_cleanup(struct bcm43xx_private *bcm)28152815+{28162816+ bcm43xx_chip_cleanup(bcm);28172817+ bcm43xx_pio_free(bcm);28182818+ bcm43xx_dma_free(bcm);28192819+28202820+ bcm->current_core->initialized = 0;28212821+}28222822+28232823+/* http://bcm-specs.sipsolutions.net/80211Init */28242824+static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm)28252825+{28262826+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);28272827+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);28282828+ u32 ucodeflags;28292829+ int err;28302830+ u32 sbimconfiglow;28312831+ u8 limit;28322832+28332833+ if (bcm->chip_rev < 5) {28342834+ sbimconfiglow = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW);28352835+ sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK;28362836+ sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK;28372837+ if (bcm->bustype == BCM43xx_BUSTYPE_PCI)28382838+ sbimconfiglow |= 0x32;28392839+ else if (bcm->bustype == BCM43xx_BUSTYPE_SB)28402840+ sbimconfiglow |= 0x53;28412841+ else28422842+ assert(0);28432843+ bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, sbimconfiglow);28442844+ }28452845+28462846+ bcm43xx_phy_calibrate(bcm);28472847+ err = bcm43xx_chip_init(bcm);28482848+ if (err)28492849+ goto out;28502850+28512851+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0016, bcm->current_core->rev);28522852+ ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, BCM43xx_UCODEFLAGS_OFFSET);28532853+28542854+ if (0 /*FIXME: which condition has to be used here? */)28552855+ ucodeflags |= 0x00000010;28562856+28572857+ /* HW decryption needs to be set now */28582858+ ucodeflags |= 0x40000000;28592859+28602860+ if (phy->type == BCM43xx_PHYTYPE_G) {28612861+ ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY;28622862+ if (phy->rev == 1)28632863+ ucodeflags |= BCM43xx_UCODEFLAG_UNKGPHY;28642864+ if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)28652865+ ucodeflags |= BCM43xx_UCODEFLAG_UNKPACTRL;28662866+ } else if (phy->type == BCM43xx_PHYTYPE_B) {28672867+ ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY;28682868+ if (phy->rev >= 2 && radio->version == 0x2050)28692869+ ucodeflags &= ~BCM43xx_UCODEFLAG_UNKGPHY;28702870+ }28712871+28722872+ if (ucodeflags != bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,28732873+ BCM43xx_UCODEFLAGS_OFFSET)) {28742874+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,28752875+ BCM43xx_UCODEFLAGS_OFFSET, ucodeflags);28762876+ }28772877+28782878+ /* Short/Long Retry Limit.28792879+ * The retry-limit is a 4-bit counter. Enforce this to avoid overflowing28802880+ * the chip-internal counter.28812881+ */28822882+ limit = limit_value(modparam_short_retry, 0, 0xF);28832883+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0006, limit);28842884+ limit = limit_value(modparam_long_retry, 0, 0xF);28852885+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0007, limit);28862886+28872887+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0044, 3);28882888+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0046, 2);28892889+28902890+ bcm43xx_rate_memory_init(bcm);28912891+28922892+ /* Minimum Contention Window */28932893+ if (phy->type == BCM43xx_PHYTYPE_B)28942894+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000001f);28952895+ else28962896+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000000f);28972897+ /* Maximum Contention Window */28982898+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff);28992899+29002900+ bcm43xx_gen_bssid(bcm);29012901+ bcm43xx_write_mac_bssid_templates(bcm);29022902+29032903+ if (bcm->current_core->rev >= 5)29042904+ bcm43xx_write16(bcm, 0x043C, 0x000C);29052905+29062906+ if (bcm43xx_using_pio(bcm))29072907+ err = bcm43xx_pio_init(bcm);29082908+ else29092909+ err = bcm43xx_dma_init(bcm);29102910+ if (err)29112911+ goto err_chip_cleanup;29122912+ bcm43xx_write16(bcm, 0x0612, 0x0050);29132913+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0416, 0x0050);29142914+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0414, 0x01F4);29152915+29162916+ bcm43xx_mac_enable(bcm);29172917+ bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate);29182918+29192919+ bcm->current_core->initialized = 1;29202920+out:29212921+ return err;29222922+29232923+err_chip_cleanup:29242924+ bcm43xx_chip_cleanup(bcm);29252925+ goto out;29262926+}29272927+29282928+static int bcm43xx_chipset_attach(struct bcm43xx_private *bcm)29292929+{29302930+ int err;29312931+ u16 pci_status;29322932+29332933+ err = bcm43xx_pctl_set_crystal(bcm, 1);29342934+ if (err)29352935+ goto out;29362936+ bcm43xx_pci_read_config16(bcm, PCI_STATUS, &pci_status);29372937+ bcm43xx_pci_write_config16(bcm, PCI_STATUS, pci_status & ~PCI_STATUS_SIG_TARGET_ABORT);29382938+29392939+out:29402940+ return err;29412941+}29422942+29432943+static void bcm43xx_chipset_detach(struct bcm43xx_private *bcm)29442944+{29452945+ bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW);29462946+ bcm43xx_pctl_set_crystal(bcm, 0);29472947+}29482948+29492949+static void bcm43xx_pcicore_broadcast_value(struct bcm43xx_private *bcm,29502950+ u32 address,29512951+ u32 data)29522952+{29532953+ bcm43xx_write32(bcm, BCM43xx_PCICORE_BCAST_ADDR, address);29542954+ bcm43xx_write32(bcm, BCM43xx_PCICORE_BCAST_DATA, data);29552955+}29562956+29572957+static int bcm43xx_pcicore_commit_settings(struct bcm43xx_private *bcm)29582958+{29592959+ int err;29602960+ struct bcm43xx_coreinfo *old_core;29612961+29622962+ old_core = bcm->current_core;29632963+ err = bcm43xx_switch_core(bcm, &bcm->core_pci);29642964+ if (err)29652965+ goto out;29662966+29672967+ bcm43xx_pcicore_broadcast_value(bcm, 0xfd8, 0x00000000);29682968+29692969+ bcm43xx_switch_core(bcm, old_core);29702970+ assert(err == 0);29712971+out:29722972+ return err;29732973+}29742974+29752975+/* Make an I/O Core usable. "core_mask" is the bitmask of the cores to enable.29762976+ * To enable core 0, pass a core_mask of 1<<029772977+ */29782978+static int bcm43xx_setup_backplane_pci_connection(struct bcm43xx_private *bcm,29792979+ u32 core_mask)29802980+{29812981+ u32 backplane_flag_nr;29822982+ u32 value;29832983+ struct bcm43xx_coreinfo *old_core;29842984+ int err = 0;29852985+29862986+ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBTPSFLAG);29872987+ backplane_flag_nr = value & BCM43xx_BACKPLANE_FLAG_NR_MASK;29882988+29892989+ old_core = bcm->current_core;29902990+ err = bcm43xx_switch_core(bcm, &bcm->core_pci);29912991+ if (err)29922992+ goto out;29932993+29942994+ if (bcm->core_pci.rev < 6) {29952995+ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBINTVEC);29962996+ value |= (1 << backplane_flag_nr);29972997+ bcm43xx_write32(bcm, BCM43xx_CIR_SBINTVEC, value);29982998+ } else {29992999+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_ICR, &value);30003000+ if (err) {30013001+ printk(KERN_ERR PFX "Error: ICR setup failure!\n");30023002+ goto out_switch_back;30033003+ }30043004+ value |= core_mask << 8;30053005+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_ICR, value);30063006+ if (err) {30073007+ printk(KERN_ERR PFX "Error: ICR setup failure!\n");30083008+ goto out_switch_back;30093009+ }30103010+ }30113011+30123012+ value = bcm43xx_read32(bcm, BCM43xx_PCICORE_SBTOPCI2);30133013+ value |= BCM43xx_SBTOPCI2_PREFETCH | BCM43xx_SBTOPCI2_BURST;30143014+ bcm43xx_write32(bcm, BCM43xx_PCICORE_SBTOPCI2, value);30153015+30163016+ if (bcm->core_pci.rev < 5) {30173017+ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW);30183018+ value |= (2 << BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_SHIFT)30193019+ & BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK;30203020+ value |= (3 << BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_SHIFT)30213021+ & BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK;30223022+ bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, value);30233023+ err = bcm43xx_pcicore_commit_settings(bcm);30243024+ assert(err == 0);30253025+ }30263026+30273027+out_switch_back:30283028+ err = bcm43xx_switch_core(bcm, old_core);30293029+out:30303030+ return err;30313031+}30323032+30333033+static void bcm43xx_softmac_init(struct bcm43xx_private *bcm)30343034+{30353035+ ieee80211softmac_start(bcm->net_dev);30363036+}30373037+30383038+static void bcm43xx_periodic_every120sec(struct bcm43xx_private *bcm)30393039+{30403040+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);30413041+30423042+ if (phy->type != BCM43xx_PHYTYPE_G || phy->rev < 2)30433043+ return;30443044+30453045+ bcm43xx_mac_suspend(bcm);30463046+ bcm43xx_phy_lo_g_measure(bcm);30473047+ bcm43xx_mac_enable(bcm);30483048+}30493049+30503050+static void bcm43xx_periodic_every60sec(struct bcm43xx_private *bcm)30513051+{30523052+ bcm43xx_phy_lo_mark_all_unused(bcm);30533053+ if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {30543054+ bcm43xx_mac_suspend(bcm);30553055+ bcm43xx_calc_nrssi_slope(bcm);30563056+ bcm43xx_mac_enable(bcm);30573057+ }30583058+}30593059+30603060+static void bcm43xx_periodic_every30sec(struct bcm43xx_private *bcm)30613061+{30623062+ /* Update device statistics. */30633063+ bcm43xx_calculate_link_quality(bcm);30643064+}30653065+30663066+static void bcm43xx_periodic_every15sec(struct bcm43xx_private *bcm)30673067+{30683068+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);30693069+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);30703070+30713071+ if (phy->type == BCM43xx_PHYTYPE_G) {30723072+ //TODO: update_aci_moving_average30733073+ if (radio->aci_enable && radio->aci_wlan_automatic) {30743074+ bcm43xx_mac_suspend(bcm);30753075+ if (!radio->aci_enable && 1 /*TODO: not scanning? */) {30763076+ if (0 /*TODO: bunch of conditions*/) {30773077+ bcm43xx_radio_set_interference_mitigation(bcm,30783078+ BCM43xx_RADIO_INTERFMODE_MANUALWLAN);30793079+ }30803080+ } else if (1/*TODO*/) {30813081+ /*30823082+ if ((aci_average > 1000) && !(bcm43xx_radio_aci_scan(bcm))) {30833083+ bcm43xx_radio_set_interference_mitigation(bcm,30843084+ BCM43xx_RADIO_INTERFMODE_NONE);30853085+ }30863086+ */30873087+ }30883088+ bcm43xx_mac_enable(bcm);30893089+ } else if (radio->interfmode == BCM43xx_RADIO_INTERFMODE_NONWLAN &&30903090+ phy->rev == 1) {30913091+ //TODO: implement rev1 workaround30923092+ }30933093+ }30943094+ bcm43xx_phy_xmitpower(bcm); //FIXME: unless scanning?30953095+ //TODO for APHY (temperature?)30963096+}30973097+30983098+static void bcm43xx_periodic_task_handler(unsigned long d)30993099+{31003100+ struct bcm43xx_private *bcm = (struct bcm43xx_private *)d;31013101+ unsigned long flags;31023102+ unsigned int state;31033103+31043104+ bcm43xx_lock_mmio(bcm, flags);31053105+31063106+ assert(bcm->initialized);31073107+ state = bcm->periodic_state;31083108+ if (state % 8 == 0)31093109+ bcm43xx_periodic_every120sec(bcm);31103110+ if (state % 4 == 0)31113111+ bcm43xx_periodic_every60sec(bcm);31123112+ if (state % 2 == 0)31133113+ bcm43xx_periodic_every30sec(bcm);31143114+ bcm43xx_periodic_every15sec(bcm);31153115+ bcm->periodic_state = state + 1;31163116+31173117+ mod_timer(&bcm->periodic_tasks, jiffies + (HZ * 15));31183118+31193119+ bcm43xx_unlock_mmio(bcm, flags);31203120+}31213121+31223122+static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm)31233123+{31243124+ del_timer_sync(&bcm->periodic_tasks);31253125+}31263126+31273127+static void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm)31283128+{31293129+ struct timer_list *timer = &(bcm->periodic_tasks);31303130+31313131+ assert(bcm->initialized);31323132+ setup_timer(timer,31333133+ bcm43xx_periodic_task_handler,31343134+ (unsigned long)bcm);31353135+ timer->expires = jiffies;31363136+ add_timer(timer);31373137+}31383138+31393139+static void bcm43xx_security_init(struct bcm43xx_private *bcm)31403140+{31413141+ bcm->security_offset = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,31423142+ 0x0056) * 2;31433143+ bcm43xx_clear_keys(bcm);31443144+}31453145+31463146+/* This is the opposite of bcm43xx_init_board() */31473147+static void bcm43xx_free_board(struct bcm43xx_private *bcm)31483148+{31493149+ int i, err;31503150+ unsigned long flags;31513151+31523152+ bcm43xx_sysfs_unregister(bcm);31533153+31543154+ bcm43xx_periodic_tasks_delete(bcm);31553155+31563156+ bcm43xx_lock(bcm, flags);31573157+ bcm->initialized = 0;31583158+ bcm->shutting_down = 1;31593159+ bcm43xx_unlock(bcm, flags);31603160+31613161+ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {31623162+ if (!bcm->core_80211[i].available)31633163+ continue;31643164+ if (!bcm->core_80211[i].initialized)31653165+ continue;31663166+31673167+ err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]);31683168+ assert(err == 0);31693169+ bcm43xx_wireless_core_cleanup(bcm);31703170+ }31713171+31723172+ bcm43xx_pctl_set_crystal(bcm, 0);31733173+31743174+ bcm43xx_lock(bcm, flags);31753175+ bcm->shutting_down = 0;31763176+ bcm43xx_unlock(bcm, flags);31773177+}31783178+31793179+static int bcm43xx_init_board(struct bcm43xx_private *bcm)31803180+{31813181+ int i, err;31823182+ int connect_phy;31833183+ unsigned long flags;31843184+31853185+ might_sleep();31863186+31873187+ bcm43xx_lock(bcm, flags);31883188+ bcm->initialized = 0;31893189+ bcm->shutting_down = 0;31903190+ bcm43xx_unlock(bcm, flags);31913191+31923192+ err = bcm43xx_pctl_set_crystal(bcm, 1);31933193+ if (err)31943194+ goto out;31953195+ err = bcm43xx_pctl_init(bcm);31963196+ if (err)31973197+ goto err_crystal_off;31983198+ err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_FAST);31993199+ if (err)32003200+ goto err_crystal_off;32013201+32023202+ tasklet_enable(&bcm->isr_tasklet);32033203+ for (i = 0; i < bcm->nr_80211_available; i++) {32043204+ err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]);32053205+ assert(err != -ENODEV);32063206+ if (err)32073207+ goto err_80211_unwind;32083208+32093209+ /* Enable the selected wireless core.32103210+ * Connect PHY only on the first core.32113211+ */32123212+ if (!bcm43xx_core_enabled(bcm)) {32133213+ if (bcm->nr_80211_available == 1) {32143214+ connect_phy = bcm43xx_current_phy(bcm)->connected;32153215+ } else {32163216+ if (i == 0)32173217+ connect_phy = 1;32183218+ else32193219+ connect_phy = 0;32203220+ }32213221+ bcm43xx_wireless_core_reset(bcm, connect_phy);32223222+ }32233223+32243224+ if (i != 0)32253225+ bcm43xx_wireless_core_mark_inactive(bcm, &bcm->core_80211[0]);32263226+32273227+ err = bcm43xx_wireless_core_init(bcm);32283228+ if (err)32293229+ goto err_80211_unwind;32303230+32313231+ if (i != 0) {32323232+ bcm43xx_mac_suspend(bcm);32333233+ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);32343234+ bcm43xx_radio_turn_off(bcm);32353235+ }32363236+ }32373237+ bcm->active_80211_core = &bcm->core_80211[0];32383238+ if (bcm->nr_80211_available >= 2) {32393239+ bcm43xx_switch_core(bcm, &bcm->core_80211[0]);32403240+ bcm43xx_mac_enable(bcm);32413241+ }32423242+ bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC);32433243+ bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_SELF, (u8 *)(bcm->net_dev->dev_addr));32443244+ dprintk(KERN_INFO PFX "80211 cores initialized\n");32453245+ bcm43xx_security_init(bcm);32463246+ bcm43xx_softmac_init(bcm);32473247+32483248+ bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC);32493249+32503250+ if (bcm43xx_current_radio(bcm)->initial_channel != 0xFF) {32513251+ bcm43xx_mac_suspend(bcm);32523252+ bcm43xx_radio_selectchannel(bcm, bcm43xx_current_radio(bcm)->initial_channel, 0);32533253+ bcm43xx_mac_enable(bcm);32543254+ }32553255+32563256+ /* Initialization of the board is done. Flag it as such. */32573257+ bcm43xx_lock(bcm, flags);32583258+ bcm->initialized = 1;32593259+ bcm43xx_unlock(bcm, flags);32603260+32613261+ bcm43xx_periodic_tasks_setup(bcm);32623262+ bcm43xx_sysfs_register(bcm);32633263+ //FIXME: check for bcm43xx_sysfs_register failure. This function is a bit messy regarding unwinding, though...32643264+32653265+ assert(err == 0);32663266+out:32673267+ return err;32683268+32693269+err_80211_unwind:32703270+ tasklet_disable(&bcm->isr_tasklet);32713271+ /* unwind all 80211 initialization */32723272+ for (i = 0; i < bcm->nr_80211_available; i++) {32733273+ if (!bcm->core_80211[i].initialized)32743274+ continue;32753275+ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);32763276+ bcm43xx_wireless_core_cleanup(bcm);32773277+ }32783278+err_crystal_off:32793279+ bcm43xx_pctl_set_crystal(bcm, 0);32803280+ goto out;32813281+}32823282+32833283+static void bcm43xx_detach_board(struct bcm43xx_private *bcm)32843284+{32853285+ struct pci_dev *pci_dev = bcm->pci_dev;32863286+ int i;32873287+32883288+ bcm43xx_chipset_detach(bcm);32893289+ /* Do _not_ access the chip, after it is detached. */32903290+ iounmap(bcm->mmio_addr);32913291+32923292+ pci_release_regions(pci_dev);32933293+ pci_disable_device(pci_dev);32943294+32953295+ /* Free allocated structures/fields */32963296+ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {32973297+ kfree(bcm->core_80211_ext[i].phy._lo_pairs);32983298+ if (bcm->core_80211_ext[i].phy.dyn_tssi_tbl)32993299+ kfree(bcm->core_80211_ext[i].phy.tssi2dbm);33003300+ }33013301+} 33023302+33033303+static int bcm43xx_read_phyinfo(struct bcm43xx_private *bcm)33043304+{33053305+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);33063306+ u16 value;33073307+ u8 phy_version;33083308+ u8 phy_type;33093309+ u8 phy_rev;33103310+ int phy_rev_ok = 1;33113311+ void *p;33123312+33133313+ value = bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_VER);33143314+33153315+ phy_version = (value & 0xF000) >> 12;33163316+ phy_type = (value & 0x0F00) >> 8;33173317+ phy_rev = (value & 0x000F);33183318+33193319+ dprintk(KERN_INFO PFX "Detected PHY: Version: %x, Type %x, Revision %x\n",33203320+ phy_version, phy_type, phy_rev);33213321+33223322+ switch (phy_type) {33233323+ case BCM43xx_PHYTYPE_A:33243324+ if (phy_rev >= 4)33253325+ phy_rev_ok = 0;33263326+ /*FIXME: We need to switch the ieee->modulation, etc.. flags,33273327+ * if we switch 80211 cores after init is done.33283328+ * As we do not implement on the fly switching between33293329+ * wireless cores, I will leave this as a future task.33303330+ */33313331+ bcm->ieee->modulation = IEEE80211_OFDM_MODULATION;33323332+ bcm->ieee->mode = IEEE_A;33333333+ bcm->ieee->freq_band = IEEE80211_52GHZ_BAND |33343334+ IEEE80211_24GHZ_BAND;33353335+ break;33363336+ case BCM43xx_PHYTYPE_B:33373337+ if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6 && phy_rev != 7)33383338+ phy_rev_ok = 0;33393339+ bcm->ieee->modulation = IEEE80211_CCK_MODULATION;33403340+ bcm->ieee->mode = IEEE_B;33413341+ bcm->ieee->freq_band = IEEE80211_24GHZ_BAND;33423342+ break;33433343+ case BCM43xx_PHYTYPE_G:33443344+ if (phy_rev > 7)33453345+ phy_rev_ok = 0;33463346+ bcm->ieee->modulation = IEEE80211_OFDM_MODULATION |33473347+ IEEE80211_CCK_MODULATION;33483348+ bcm->ieee->mode = IEEE_G;33493349+ bcm->ieee->freq_band = IEEE80211_24GHZ_BAND;33503350+ break;33513351+ default:33523352+ printk(KERN_ERR PFX "Error: Unknown PHY Type %x\n",33533353+ phy_type);33543354+ return -ENODEV;33553355+ };33563356+ if (!phy_rev_ok) {33573357+ printk(KERN_WARNING PFX "Invalid PHY Revision %x\n",33583358+ phy_rev);33593359+ }33603360+33613361+ phy->version = phy_version;33623362+ phy->type = phy_type;33633363+ phy->rev = phy_rev;33643364+ if ((phy_type == BCM43xx_PHYTYPE_B) || (phy_type == BCM43xx_PHYTYPE_G)) {33653365+ p = kzalloc(sizeof(struct bcm43xx_lopair) * BCM43xx_LO_COUNT,33663366+ GFP_KERNEL);33673367+ if (!p)33683368+ return -ENOMEM;33693369+ phy->_lo_pairs = p;33703370+ }33713371+33723372+ return 0;33733373+}33743374+33753375+static int bcm43xx_attach_board(struct bcm43xx_private *bcm)33763376+{33773377+ struct pci_dev *pci_dev = bcm->pci_dev;33783378+ struct net_device *net_dev = bcm->net_dev;33793379+ int err;33803380+ int i;33813381+ unsigned long mmio_start, mmio_flags, mmio_len;33823382+ u32 coremask;33833383+33843384+ err = pci_enable_device(pci_dev);33853385+ if (err) {33863386+ printk(KERN_ERR PFX "unable to wake up pci device (%i)\n", err);33873387+ goto out;33883388+ }33893389+ mmio_start = pci_resource_start(pci_dev, 0);33903390+ mmio_flags = pci_resource_flags(pci_dev, 0);33913391+ mmio_len = pci_resource_len(pci_dev, 0);33923392+ if (!(mmio_flags & IORESOURCE_MEM)) {33933393+ printk(KERN_ERR PFX33943394+ "%s, region #0 not an MMIO resource, aborting\n",33953395+ pci_name(pci_dev));33963396+ err = -ENODEV;33973397+ goto err_pci_disable;33983398+ }33993399+ err = pci_request_regions(pci_dev, KBUILD_MODNAME);34003400+ if (err) {34013401+ printk(KERN_ERR PFX34023402+ "could not access PCI resources (%i)\n", err);34033403+ goto err_pci_disable;34043404+ }34053405+ /* enable PCI bus-mastering */34063406+ pci_set_master(pci_dev);34073407+ bcm->mmio_addr = ioremap(mmio_start, mmio_len);34083408+ if (!bcm->mmio_addr) {34093409+ printk(KERN_ERR PFX "%s: cannot remap MMIO, aborting\n",34103410+ pci_name(pci_dev));34113411+ err = -EIO;34123412+ goto err_pci_release;34133413+ }34143414+ bcm->mmio_len = mmio_len;34153415+ net_dev->base_addr = (unsigned long)bcm->mmio_addr;34163416+34173417+ bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_VENDOR_ID,34183418+ &bcm->board_vendor);34193419+ bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_ID,34203420+ &bcm->board_type);34213421+ bcm43xx_pci_read_config16(bcm, PCI_REVISION_ID,34223422+ &bcm->board_revision);34233423+34243424+ err = bcm43xx_chipset_attach(bcm);34253425+ if (err)34263426+ goto err_iounmap;34273427+ err = bcm43xx_pctl_init(bcm);34283428+ if (err)34293429+ goto err_chipset_detach;34303430+ err = bcm43xx_probe_cores(bcm);34313431+ if (err)34323432+ goto err_chipset_detach;34333433+34343434+ /* Attach all IO cores to the backplane. */34353435+ coremask = 0;34363436+ for (i = 0; i < bcm->nr_80211_available; i++)34373437+ coremask |= (1 << bcm->core_80211[i].index);34383438+ //FIXME: Also attach some non80211 cores?34393439+ err = bcm43xx_setup_backplane_pci_connection(bcm, coremask);34403440+ if (err) {34413441+ printk(KERN_ERR PFX "Backplane->PCI connection failed!\n");34423442+ goto err_chipset_detach;34433443+ }34443444+34453445+ err = bcm43xx_sprom_extract(bcm);34463446+ if (err)34473447+ goto err_chipset_detach;34483448+ err = bcm43xx_leds_init(bcm);34493449+ if (err)34503450+ goto err_chipset_detach;34513451+34523452+ for (i = 0; i < bcm->nr_80211_available; i++) {34533453+ err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]);34543454+ assert(err != -ENODEV);34553455+ if (err)34563456+ goto err_80211_unwind;34573457+34583458+ /* Enable the selected wireless core.34593459+ * Connect PHY only on the first core.34603460+ */34613461+ bcm43xx_wireless_core_reset(bcm, (i == 0));34623462+34633463+ err = bcm43xx_read_phyinfo(bcm);34643464+ if (err && (i == 0))34653465+ goto err_80211_unwind;34663466+34673467+ err = bcm43xx_read_radioinfo(bcm);34683468+ if (err && (i == 0))34693469+ goto err_80211_unwind;34703470+34713471+ err = bcm43xx_validate_chip(bcm);34723472+ if (err && (i == 0))34733473+ goto err_80211_unwind;34743474+34753475+ bcm43xx_radio_turn_off(bcm);34763476+ err = bcm43xx_phy_init_tssi2dbm_table(bcm);34773477+ if (err)34783478+ goto err_80211_unwind;34793479+ bcm43xx_wireless_core_disable(bcm);34803480+ }34813481+ bcm43xx_pctl_set_crystal(bcm, 0);34823482+34833483+ /* Set the MAC address in the networking subsystem */34843484+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)34853485+ memcpy(bcm->net_dev->dev_addr, bcm->sprom.et1macaddr, 6);34863486+ else34873487+ memcpy(bcm->net_dev->dev_addr, bcm->sprom.il0macaddr, 6);34883488+34893489+ bcm43xx_geo_init(bcm);34903490+34913491+ snprintf(bcm->nick, IW_ESSID_MAX_SIZE,34923492+ "Broadcom %04X", bcm->chip_id);34933493+34943494+ assert(err == 0);34953495+out:34963496+ return err;34973497+34983498+err_80211_unwind:34993499+ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {35003500+ kfree(bcm->core_80211_ext[i].phy._lo_pairs);35013501+ if (bcm->core_80211_ext[i].phy.dyn_tssi_tbl)35023502+ kfree(bcm->core_80211_ext[i].phy.tssi2dbm);35033503+ }35043504+err_chipset_detach:35053505+ bcm43xx_chipset_detach(bcm);35063506+err_iounmap:35073507+ iounmap(bcm->mmio_addr);35083508+err_pci_release:35093509+ pci_release_regions(pci_dev);35103510+err_pci_disable:35113511+ pci_disable_device(pci_dev);35123512+ goto out;35133513+}35143514+35153515+/* Do the Hardware IO operations to send the txb */35163516+static inline int bcm43xx_tx(struct bcm43xx_private *bcm,35173517+ struct ieee80211_txb *txb)35183518+{35193519+ int err = -ENODEV;35203520+35213521+ if (bcm43xx_using_pio(bcm))35223522+ err = bcm43xx_pio_tx(bcm, txb);35233523+ else35243524+ err = bcm43xx_dma_tx(bcm, txb);35253525+35263526+ return err;35273527+}35283528+35293529+static void bcm43xx_ieee80211_set_chan(struct net_device *net_dev,35303530+ u8 channel)35313531+{35323532+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);35333533+ struct bcm43xx_radioinfo *radio;35343534+ unsigned long flags;35353535+35363536+ bcm43xx_lock_mmio(bcm, flags);35373537+ if (bcm->initialized) {35383538+ bcm43xx_mac_suspend(bcm);35393539+ bcm43xx_radio_selectchannel(bcm, channel, 0);35403540+ bcm43xx_mac_enable(bcm);35413541+ } else {35423542+ radio = bcm43xx_current_radio(bcm);35433543+ radio->initial_channel = channel;35443544+ }35453545+ bcm43xx_unlock_mmio(bcm, flags);35463546+}35473547+35483548+/* set_security() callback in struct ieee80211_device */35493549+static void bcm43xx_ieee80211_set_security(struct net_device *net_dev,35503550+ struct ieee80211_security *sec)35513551+{35523552+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);35533553+ struct ieee80211_security *secinfo = &bcm->ieee->sec;35543554+ unsigned long flags;35553555+ int keyidx;35563556+35573557+ dprintk(KERN_INFO PFX "set security called\n");35583558+35593559+ bcm43xx_lock_mmio(bcm, flags);35603560+35613561+ for (keyidx = 0; keyidx<WEP_KEYS; keyidx++)35623562+ if (sec->flags & (1<<keyidx)) {35633563+ secinfo->encode_alg[keyidx] = sec->encode_alg[keyidx];35643564+ secinfo->key_sizes[keyidx] = sec->key_sizes[keyidx];35653565+ memcpy(secinfo->keys[keyidx], sec->keys[keyidx], SCM_KEY_LEN);35663566+ }35673567+35683568+ if (sec->flags & SEC_ACTIVE_KEY) {35693569+ secinfo->active_key = sec->active_key;35703570+ dprintk(KERN_INFO PFX " .active_key = %d\n", sec->active_key);35713571+ }35723572+ if (sec->flags & SEC_UNICAST_GROUP) {35733573+ secinfo->unicast_uses_group = sec->unicast_uses_group;35743574+ dprintk(KERN_INFO PFX " .unicast_uses_group = %d\n", sec->unicast_uses_group);35753575+ }35763576+ if (sec->flags & SEC_LEVEL) {35773577+ secinfo->level = sec->level;35783578+ dprintk(KERN_INFO PFX " .level = %d\n", sec->level);35793579+ }35803580+ if (sec->flags & SEC_ENABLED) {35813581+ secinfo->enabled = sec->enabled;35823582+ dprintk(KERN_INFO PFX " .enabled = %d\n", sec->enabled);35833583+ }35843584+ if (sec->flags & SEC_ENCRYPT) {35853585+ secinfo->encrypt = sec->encrypt;35863586+ dprintk(KERN_INFO PFX " .encrypt = %d\n", sec->encrypt);35873587+ }35883588+ if (bcm->initialized && !bcm->ieee->host_encrypt) {35893589+ if (secinfo->enabled) {35903590+ /* upload WEP keys to hardware */35913591+ char null_address[6] = { 0 };35923592+ u8 algorithm = 0;35933593+ for (keyidx = 0; keyidx<WEP_KEYS; keyidx++) {35943594+ if (!(sec->flags & (1<<keyidx)))35953595+ continue;35963596+ switch (sec->encode_alg[keyidx]) {35973597+ case SEC_ALG_NONE: algorithm = BCM43xx_SEC_ALGO_NONE; break;35983598+ case SEC_ALG_WEP:35993599+ algorithm = BCM43xx_SEC_ALGO_WEP;36003600+ if (secinfo->key_sizes[keyidx] == 13)36013601+ algorithm = BCM43xx_SEC_ALGO_WEP104;36023602+ break;36033603+ case SEC_ALG_TKIP:36043604+ FIXME();36053605+ algorithm = BCM43xx_SEC_ALGO_TKIP;36063606+ break;36073607+ case SEC_ALG_CCMP:36083608+ FIXME();36093609+ algorithm = BCM43xx_SEC_ALGO_AES;36103610+ break;36113611+ default:36123612+ assert(0);36133613+ break;36143614+ }36153615+ bcm43xx_key_write(bcm, keyidx, algorithm, sec->keys[keyidx], secinfo->key_sizes[keyidx], &null_address[0]);36163616+ bcm->key[keyidx].enabled = 1;36173617+ bcm->key[keyidx].algorithm = algorithm;36183618+ }36193619+ } else36203620+ bcm43xx_clear_keys(bcm);36213621+ }36223622+ bcm43xx_unlock_mmio(bcm, flags);36233623+}36243624+36253625+/* hard_start_xmit() callback in struct ieee80211_device */36263626+static int bcm43xx_ieee80211_hard_start_xmit(struct ieee80211_txb *txb,36273627+ struct net_device *net_dev,36283628+ int pri)36293629+{36303630+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);36313631+ int err = -ENODEV;36323632+ unsigned long flags;36333633+36343634+ bcm43xx_lock_mmio(bcm, flags);36353635+ if (likely(bcm->initialized))36363636+ err = bcm43xx_tx(bcm, txb);36373637+ bcm43xx_unlock_mmio(bcm, flags);36383638+36393639+ return err;36403640+}36413641+36423642+static struct net_device_stats * bcm43xx_net_get_stats(struct net_device *net_dev)36433643+{36443644+ return &(bcm43xx_priv(net_dev)->ieee->stats);36453645+}36463646+36473647+static void bcm43xx_net_tx_timeout(struct net_device *net_dev)36483648+{36493649+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);36503650+ unsigned long flags;36513651+36523652+ bcm43xx_lock_mmio(bcm, flags);36533653+ bcm43xx_controller_restart(bcm, "TX timeout");36543654+ bcm43xx_unlock_mmio(bcm, flags);36553655+}36563656+36573657+#ifdef CONFIG_NET_POLL_CONTROLLER36583658+static void bcm43xx_net_poll_controller(struct net_device *net_dev)36593659+{36603660+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);36613661+ unsigned long flags;36623662+36633663+ local_irq_save(flags);36643664+ bcm43xx_interrupt_handler(bcm->irq, bcm, NULL);36653665+ local_irq_restore(flags);36663666+}36673667+#endif /* CONFIG_NET_POLL_CONTROLLER */36683668+36693669+static int bcm43xx_net_open(struct net_device *net_dev)36703670+{36713671+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);36723672+36733673+ return bcm43xx_init_board(bcm);36743674+}36753675+36763676+static int bcm43xx_net_stop(struct net_device *net_dev)36773677+{36783678+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);36793679+36803680+ ieee80211softmac_stop(net_dev);36813681+ bcm43xx_disable_interrupts_sync(bcm, NULL);36823682+ bcm43xx_free_board(bcm);36833683+36843684+ return 0;36853685+}36863686+36873687+static int bcm43xx_init_private(struct bcm43xx_private *bcm,36883688+ struct net_device *net_dev,36893689+ struct pci_dev *pci_dev)36903690+{36913691+ int err;36923692+36933693+ bcm->ieee = netdev_priv(net_dev);36943694+ bcm->softmac = ieee80211_priv(net_dev);36953695+ bcm->softmac->set_channel = bcm43xx_ieee80211_set_chan;36963696+36973697+ bcm->irq_savedstate = BCM43xx_IRQ_INITIAL;36983698+ bcm->pci_dev = pci_dev;36993699+ bcm->net_dev = net_dev;37003700+ bcm->bad_frames_preempt = modparam_bad_frames_preempt;37013701+ spin_lock_init(&bcm->_lock);37023702+ tasklet_init(&bcm->isr_tasklet,37033703+ (void (*)(unsigned long))bcm43xx_interrupt_tasklet,37043704+ (unsigned long)bcm);37053705+ tasklet_disable_nosync(&bcm->isr_tasklet);37063706+ if (modparam_pio) {37073707+ bcm->__using_pio = 1;37083708+ } else {37093709+ err = pci_set_dma_mask(pci_dev, DMA_30BIT_MASK);37103710+ err |= pci_set_consistent_dma_mask(pci_dev, DMA_30BIT_MASK);37113711+ if (err) {37123712+#ifdef CONFIG_BCM43XX_PIO37133713+ printk(KERN_WARNING PFX "DMA not supported. Falling back to PIO.\n");37143714+ bcm->__using_pio = 1;37153715+#else37163716+ printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. "37173717+ "Recompile the driver with PIO support, please.\n");37183718+ return -ENODEV;37193719+#endif /* CONFIG_BCM43XX_PIO */37203720+ }37213721+ }37223722+ bcm->rts_threshold = BCM43xx_DEFAULT_RTS_THRESHOLD;37233723+37243724+ /* default to sw encryption for now */37253725+ bcm->ieee->host_build_iv = 0;37263726+ bcm->ieee->host_encrypt = 1;37273727+ bcm->ieee->host_decrypt = 1;37283728+37293729+ bcm->ieee->iw_mode = BCM43xx_INITIAL_IWMODE;37303730+ bcm->ieee->tx_headroom = sizeof(struct bcm43xx_txhdr);37313731+ bcm->ieee->set_security = bcm43xx_ieee80211_set_security;37323732+ bcm->ieee->hard_start_xmit = bcm43xx_ieee80211_hard_start_xmit;37333733+37343734+ return 0;37353735+}37363736+37373737+static int __devinit bcm43xx_init_one(struct pci_dev *pdev,37383738+ const struct pci_device_id *ent)37393739+{37403740+ struct net_device *net_dev;37413741+ struct bcm43xx_private *bcm;37423742+ int err;37433743+37443744+#ifdef CONFIG_BCM947XX37453745+ if ((pdev->bus->number == 0) && (pdev->device != 0x0800))37463746+ return -ENODEV;37473747+#endif37483748+37493749+#ifdef DEBUG_SINGLE_DEVICE_ONLY37503750+ if (strcmp(pci_name(pdev), DEBUG_SINGLE_DEVICE_ONLY))37513751+ return -ENODEV;37523752+#endif37533753+37543754+ net_dev = alloc_ieee80211softmac(sizeof(*bcm));37553755+ if (!net_dev) {37563756+ printk(KERN_ERR PFX37573757+ "could not allocate ieee80211 device %s\n",37583758+ pci_name(pdev));37593759+ err = -ENOMEM;37603760+ goto out;37613761+ }37623762+ /* initialize the net_device struct */37633763+ SET_MODULE_OWNER(net_dev);37643764+ SET_NETDEV_DEV(net_dev, &pdev->dev);37653765+37663766+ net_dev->open = bcm43xx_net_open;37673767+ net_dev->stop = bcm43xx_net_stop;37683768+ net_dev->get_stats = bcm43xx_net_get_stats;37693769+ net_dev->tx_timeout = bcm43xx_net_tx_timeout;37703770+#ifdef CONFIG_NET_POLL_CONTROLLER37713771+ net_dev->poll_controller = bcm43xx_net_poll_controller;37723772+#endif37733773+ net_dev->wireless_handlers = &bcm43xx_wx_handlers_def;37743774+ net_dev->irq = pdev->irq;37753775+ SET_ETHTOOL_OPS(net_dev, &bcm43xx_ethtool_ops);37763776+37773777+ /* initialize the bcm43xx_private struct */37783778+ bcm = bcm43xx_priv(net_dev);37793779+ memset(bcm, 0, sizeof(*bcm));37803780+ err = bcm43xx_init_private(bcm, net_dev, pdev);37813781+ if (err)37823782+ goto err_free_netdev;37833783+37843784+ pci_set_drvdata(pdev, net_dev);37853785+37863786+ err = bcm43xx_attach_board(bcm);37873787+ if (err)37883788+ goto err_free_netdev;37893789+37903790+ err = register_netdev(net_dev);37913791+ if (err) {37923792+ printk(KERN_ERR PFX "Cannot register net device, "37933793+ "aborting.\n");37943794+ err = -ENOMEM;37953795+ goto err_detach_board;37963796+ }37973797+37983798+ bcm43xx_debugfs_add_device(bcm);37993799+38003800+ assert(err == 0);38013801+out:38023802+ return err;38033803+38043804+err_detach_board:38053805+ bcm43xx_detach_board(bcm);38063806+err_free_netdev:38073807+ free_ieee80211softmac(net_dev);38083808+ goto out;38093809+}38103810+38113811+static void __devexit bcm43xx_remove_one(struct pci_dev *pdev)38123812+{38133813+ struct net_device *net_dev = pci_get_drvdata(pdev);38143814+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);38153815+38163816+ bcm43xx_debugfs_remove_device(bcm);38173817+ unregister_netdev(net_dev);38183818+ bcm43xx_detach_board(bcm);38193819+ assert(bcm->ucode == NULL);38203820+ free_ieee80211softmac(net_dev);38213821+}38223822+38233823+/* Hard-reset the chip. Do not call this directly.38243824+ * Use bcm43xx_controller_restart()38253825+ */38263826+static void bcm43xx_chip_reset(void *_bcm)38273827+{38283828+ struct bcm43xx_private *bcm = _bcm;38293829+ struct net_device *net_dev = bcm->net_dev;38303830+ struct pci_dev *pci_dev = bcm->pci_dev;38313831+ int err;38323832+ int was_initialized = bcm->initialized;38333833+38343834+ netif_stop_queue(bcm->net_dev);38353835+ tasklet_disable(&bcm->isr_tasklet);38363836+38373837+ bcm->firmware_norelease = 1;38383838+ if (was_initialized)38393839+ bcm43xx_free_board(bcm);38403840+ bcm->firmware_norelease = 0;38413841+ bcm43xx_detach_board(bcm);38423842+ err = bcm43xx_init_private(bcm, net_dev, pci_dev);38433843+ if (err)38443844+ goto failure;38453845+ err = bcm43xx_attach_board(bcm);38463846+ if (err)38473847+ goto failure;38483848+ if (was_initialized) {38493849+ err = bcm43xx_init_board(bcm);38503850+ if (err)38513851+ goto failure;38523852+ }38533853+ netif_wake_queue(bcm->net_dev);38543854+ printk(KERN_INFO PFX "Controller restarted\n");38553855+38563856+ return;38573857+failure:38583858+ printk(KERN_ERR PFX "Controller restart failed\n");38593859+}38603860+38613861+/* Hard-reset the chip.38623862+ * This can be called from interrupt or process context.38633863+ * Make sure to _not_ re-enable device interrupts after this has been called.38643864+*/38653865+void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason)38663866+{38673867+ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);38683868+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */38693869+ printk(KERN_ERR PFX "Controller RESET (%s) ...\n", reason);38703870+ INIT_WORK(&bcm->restart_work, bcm43xx_chip_reset, bcm);38713871+ schedule_work(&bcm->restart_work);38723872+}38733873+38743874+#ifdef CONFIG_PM38753875+38763876+static int bcm43xx_suspend(struct pci_dev *pdev, pm_message_t state)38773877+{38783878+ struct net_device *net_dev = pci_get_drvdata(pdev);38793879+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);38803880+ unsigned long flags;38813881+ int try_to_shutdown = 0, err;38823882+38833883+ dprintk(KERN_INFO PFX "Suspending...\n");38843884+38853885+ bcm43xx_lock(bcm, flags);38863886+ bcm->was_initialized = bcm->initialized;38873887+ if (bcm->initialized)38883888+ try_to_shutdown = 1;38893889+ bcm43xx_unlock(bcm, flags);38903890+38913891+ netif_device_detach(net_dev);38923892+ if (try_to_shutdown) {38933893+ ieee80211softmac_stop(net_dev);38943894+ err = bcm43xx_disable_interrupts_sync(bcm, &bcm->irq_savedstate);38953895+ if (unlikely(err)) {38963896+ dprintk(KERN_ERR PFX "Suspend failed.\n");38973897+ return -EAGAIN;38983898+ }38993899+ bcm->firmware_norelease = 1;39003900+ bcm43xx_free_board(bcm);39013901+ bcm->firmware_norelease = 0;39023902+ }39033903+ bcm43xx_chipset_detach(bcm);39043904+39053905+ pci_save_state(pdev);39063906+ pci_disable_device(pdev);39073907+ pci_set_power_state(pdev, pci_choose_state(pdev, state));39083908+39093909+ dprintk(KERN_INFO PFX "Device suspended.\n");39103910+39113911+ return 0;39123912+}39133913+39143914+static int bcm43xx_resume(struct pci_dev *pdev)39153915+{39163916+ struct net_device *net_dev = pci_get_drvdata(pdev);39173917+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);39183918+ int err = 0;39193919+39203920+ dprintk(KERN_INFO PFX "Resuming...\n");39213921+39223922+ pci_set_power_state(pdev, 0);39233923+ pci_enable_device(pdev);39243924+ pci_restore_state(pdev);39253925+39263926+ bcm43xx_chipset_attach(bcm);39273927+ if (bcm->was_initialized) {39283928+ bcm->irq_savedstate = BCM43xx_IRQ_INITIAL;39293929+ err = bcm43xx_init_board(bcm);39303930+ }39313931+ if (err) {39323932+ printk(KERN_ERR PFX "Resume failed!\n");39333933+ return err;39343934+ }39353935+39363936+ netif_device_attach(net_dev);39373937+39383938+ /*FIXME: This should be handled by softmac instead. */39393939+ schedule_work(&bcm->softmac->associnfo.work);39403940+39413941+ dprintk(KERN_INFO PFX "Device resumed.\n");39423942+39433943+ return 0;39443944+}39453945+39463946+#endif /* CONFIG_PM */39473947+39483948+static struct pci_driver bcm43xx_pci_driver = {39493949+ .name = KBUILD_MODNAME,39503950+ .id_table = bcm43xx_pci_tbl,39513951+ .probe = bcm43xx_init_one,39523952+ .remove = __devexit_p(bcm43xx_remove_one),39533953+#ifdef CONFIG_PM39543954+ .suspend = bcm43xx_suspend,39553955+ .resume = bcm43xx_resume,39563956+#endif /* CONFIG_PM */39573957+};39583958+39593959+static int __init bcm43xx_init(void)39603960+{39613961+ printk(KERN_INFO KBUILD_MODNAME " driver\n");39623962+ bcm43xx_debugfs_init();39633963+ return pci_register_driver(&bcm43xx_pci_driver);39643964+}39653965+39663966+static void __exit bcm43xx_exit(void)39673967+{39683968+ pci_unregister_driver(&bcm43xx_pci_driver);39693969+ bcm43xx_debugfs_exit();39703970+}39713971+39723972+module_init(bcm43xx_init)39733973+module_exit(bcm43xx_exit)
+168
drivers/net/wireless/bcm43xx/bcm43xx_main.h
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,66+ Stefano Brivio <st3@riseup.net>77+ Michael Buesch <mbuesch@freenet.de>88+ Danny van Dyk <kugelfang@gentoo.org>99+ Andreas Jaggi <andreas.jaggi@waterwave.ch>1010+1111+ Some parts of the code in this file are derived from the ipw22001212+ driver Copyright(c) 2003 - 2004 Intel Corporation.1313+1414+ This program is free software; you can redistribute it and/or modify1515+ it under the terms of the GNU General Public License as published by1616+ the Free Software Foundation; either version 2 of the License, or1717+ (at your option) any later version.1818+1919+ This program is distributed in the hope that it will be useful,2020+ but WITHOUT ANY WARRANTY; without even the implied warranty of2121+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the2222+ GNU General Public License for more details.2323+2424+ You should have received a copy of the GNU General Public License2525+ along with this program; see the file COPYING. If not, write to2626+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2727+ Boston, MA 02110-1301, USA.2828+2929+*/3030+3131+#ifndef BCM43xx_MAIN_H_3232+#define BCM43xx_MAIN_H_3333+3434+#include "bcm43xx.h"3535+3636+#ifdef CONFIG_BCM947XX3737+#define atoi(str) simple_strtoul(((str != NULL) ? str : ""), NULL, 0)3838+3939+static inline void e_aton(char *str, char *dest)4040+{4141+ int i = 0;4242+ u16 *d = (u16 *) dest;4343+4444+ for (;;) {4545+ dest[i++] = (char) simple_strtoul(str, NULL, 16);4646+ str += 2;4747+ if (!*str++ || i == 6)4848+ break;4949+ }5050+ for (i = 0; i < 3; i++)5151+ d[i] = cpu_to_be16(d[i]);5252+}5353+#endif5454+5555+#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes]5656+#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes)5757+/* Magic helper macro to pad structures. Ignore those above. It's magic. */5858+#define PAD_BYTES(nr_bytes) P4D_BYTES( __LINE__ , (nr_bytes))5959+6060+6161+/* Lightweight function to convert a frequency (in Mhz) to a channel number. */6262+static inline6363+u8 bcm43xx_freq_to_channel_a(int freq)6464+{6565+ return ((freq - 5000) / 5);6666+}6767+static inline6868+u8 bcm43xx_freq_to_channel_bg(int freq)6969+{7070+ u8 channel;7171+7272+ if (freq == 2484)7373+ channel = 14;7474+ else7575+ channel = (freq - 2407) / 5;7676+7777+ return channel;7878+}7979+static inline8080+u8 bcm43xx_freq_to_channel(struct bcm43xx_private *bcm,8181+ int freq)8282+{8383+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)8484+ return bcm43xx_freq_to_channel_a(freq);8585+ return bcm43xx_freq_to_channel_bg(freq);8686+}8787+8888+/* Lightweight function to convert a channel number to a frequency (in Mhz). */8989+static inline9090+int bcm43xx_channel_to_freq_a(u8 channel)9191+{9292+ return (5000 + (5 * channel));9393+}9494+static inline9595+int bcm43xx_channel_to_freq_bg(u8 channel)9696+{9797+ int freq;9898+9999+ if (channel == 14)100100+ freq = 2484;101101+ else102102+ freq = 2407 + (5 * channel);103103+104104+ return freq;105105+}106106+static inline107107+int bcm43xx_channel_to_freq(struct bcm43xx_private *bcm,108108+ u8 channel)109109+{110110+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)111111+ return bcm43xx_channel_to_freq_a(channel);112112+ return bcm43xx_channel_to_freq_bg(channel);113113+}114114+115115+/* Lightweight function to check if a channel number is valid.116116+ * Note that this does _NOT_ check for geographical restrictions!117117+ */118118+static inline119119+int bcm43xx_is_valid_channel_a(u8 channel)120120+{121121+ return (channel <= 200);122122+}123123+static inline124124+int bcm43xx_is_valid_channel_bg(u8 channel)125125+{126126+ return (channel >= 1 && channel <= 14);127127+}128128+static inline129129+int bcm43xx_is_valid_channel(struct bcm43xx_private *bcm,130130+ u8 channel)131131+{132132+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)133133+ return bcm43xx_is_valid_channel_a(channel);134134+ return bcm43xx_is_valid_channel_bg(channel);135135+}136136+137137+void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf);138138+void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf);139139+140140+void bcm43xx_set_iwmode(struct bcm43xx_private *bcm,141141+ int iw_mode);142142+143143+u32 bcm43xx_shm_read32(struct bcm43xx_private *bcm,144144+ u16 routing, u16 offset);145145+u16 bcm43xx_shm_read16(struct bcm43xx_private *bcm,146146+ u16 routing, u16 offset);147147+void bcm43xx_shm_write32(struct bcm43xx_private *bcm,148148+ u16 routing, u16 offset,149149+ u32 value);150150+void bcm43xx_shm_write16(struct bcm43xx_private *bcm,151151+ u16 routing, u16 offset,152152+ u16 value);153153+154154+void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm);155155+156156+int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core);157157+158158+void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy);159159+160160+void bcm43xx_mac_suspend(struct bcm43xx_private *bcm);161161+void bcm43xx_mac_enable(struct bcm43xx_private *bcm);162162+163163+void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason);164164+165165+int bcm43xx_sprom_read(struct bcm43xx_private *bcm, u16 *sprom);166166+int bcm43xx_sprom_write(struct bcm43xx_private *bcm, const u16 *sprom);167167+168168+#endif /* BCM43xx_MAIN_H_ */
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,66+ Stefano Brivio <st3@riseup.net>77+ Michael Buesch <mbuesch@freenet.de>88+ Danny van Dyk <kugelfang@gentoo.org>99+ Andreas Jaggi <andreas.jaggi@waterwave.ch>1010+1111+ Some parts of the code in this file are derived from the ipw22001212+ driver Copyright(c) 2003 - 2004 Intel Corporation.1313+1414+ This program is free software; you can redistribute it and/or modify1515+ it under the terms of the GNU General Public License as published by1616+ the Free Software Foundation; either version 2 of the License, or1717+ (at your option) any later version.1818+1919+ This program is distributed in the hope that it will be useful,2020+ but WITHOUT ANY WARRANTY; without even the implied warranty of2121+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the2222+ GNU General Public License for more details.2323+2424+ You should have received a copy of the GNU General Public License2525+ along with this program; see the file COPYING. If not, write to2626+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2727+ Boston, MA 02110-1301, USA.2828+2929+*/3030+3131+#ifndef BCM43xx_PHY_H_3232+#define BCM43xx_PHY_H_3333+3434+#include <linux/types.h>3535+3636+struct bcm43xx_private;3737+3838+void bcm43xx_raw_phy_lock(struct bcm43xx_private *bcm);3939+#define bcm43xx_phy_lock(bcm, flags) \4040+ do { \4141+ local_irq_save(flags); \4242+ bcm43xx_raw_phy_lock(bcm); \4343+ } while (0)4444+void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm);4545+#define bcm43xx_phy_unlock(bcm, flags) \4646+ do { \4747+ bcm43xx_raw_phy_unlock(bcm); \4848+ local_irq_restore(flags); \4949+ } while (0)5050+5151+u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset);5252+void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val);5353+5454+int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_private *bcm);5555+int bcm43xx_phy_init(struct bcm43xx_private *bcm);5656+5757+void bcm43xx_phy_set_antenna_diversity(struct bcm43xx_private *bcm);5858+void bcm43xx_phy_calibrate(struct bcm43xx_private *bcm);5959+int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect);6060+6161+void bcm43xx_phy_lo_b_measure(struct bcm43xx_private *bcm);6262+void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm);6363+void bcm43xx_phy_xmitpower(struct bcm43xx_private *bcm);6464+6565+/* Adjust the LocalOscillator to the saved values.6666+ * "fixed" is only set to 1 once in initialization. Set to 0 otherwise.6767+ */6868+void bcm43xx_phy_lo_adjust(struct bcm43xx_private *bcm, int fixed);6969+void bcm43xx_phy_lo_mark_all_unused(struct bcm43xx_private *bcm);7070+7171+void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm,7272+ u16 baseband_attenuation);7373+7474+#endif /* BCM43xx_PHY_H_ */
+606
drivers/net/wireless/bcm43xx/bcm43xx_pio.c
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ PIO Transmission66+77+ Copyright (c) 2005 Michael Buesch <mbuesch@freenet.de>88+99+ This program is free software; you can redistribute it and/or modify1010+ it under the terms of the GNU General Public License as published by1111+ the Free Software Foundation; either version 2 of the License, or1212+ (at your option) any later version.1313+1414+ This program is distributed in the hope that it will be useful,1515+ but WITHOUT ANY WARRANTY; without even the implied warranty of1616+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1717+ GNU General Public License for more details.1818+1919+ You should have received a copy of the GNU General Public License2020+ along with this program; see the file COPYING. If not, write to2121+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2222+ Boston, MA 02110-1301, USA.2323+2424+*/2525+2626+#include "bcm43xx.h"2727+#include "bcm43xx_pio.h"2828+#include "bcm43xx_main.h"2929+#include "bcm43xx_xmit.h"3030+3131+#include <linux/delay.h>3232+3333+3434+static void tx_start(struct bcm43xx_pioqueue *queue)3535+{3636+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,3737+ BCM43xx_PIO_TXCTL_INIT);3838+}3939+4040+static void tx_octet(struct bcm43xx_pioqueue *queue,4141+ u8 octet)4242+{4343+ if (queue->need_workarounds) {4444+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,4545+ octet);4646+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,4747+ BCM43xx_PIO_TXCTL_WRITEHI);4848+ } else {4949+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,5050+ BCM43xx_PIO_TXCTL_WRITEHI);5151+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,5252+ octet);5353+ }5454+}5555+5656+static u16 tx_get_next_word(struct bcm43xx_txhdr *txhdr,5757+ const u8 *packet,5858+ unsigned int *pos)5959+{6060+ const u8 *source;6161+ unsigned int i = *pos;6262+ u16 ret;6363+6464+ if (i < sizeof(*txhdr)) {6565+ source = (const u8 *)txhdr;6666+ } else {6767+ source = packet;6868+ i -= sizeof(*txhdr);6969+ }7070+ ret = le16_to_cpu( *((u16 *)(source + i)) );7171+ *pos += 2;7272+7373+ return ret;7474+}7575+7676+static void tx_data(struct bcm43xx_pioqueue *queue,7777+ struct bcm43xx_txhdr *txhdr,7878+ const u8 *packet,7979+ unsigned int octets)8080+{8181+ u16 data;8282+ unsigned int i = 0;8383+8484+ if (queue->need_workarounds) {8585+ data = tx_get_next_word(txhdr, packet, &i);8686+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data);8787+ }8888+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,8989+ BCM43xx_PIO_TXCTL_WRITELO |9090+ BCM43xx_PIO_TXCTL_WRITEHI);9191+ while (i < octets - 1) {9292+ data = tx_get_next_word(txhdr, packet, &i);9393+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data);9494+ }9595+ if (octets % 2)9696+ tx_octet(queue, packet[octets - sizeof(*txhdr) - 1]);9797+}9898+9999+static void tx_complete(struct bcm43xx_pioqueue *queue,100100+ struct sk_buff *skb)101101+{102102+ if (queue->need_workarounds) {103103+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,104104+ skb->data[skb->len - 1]);105105+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,106106+ BCM43xx_PIO_TXCTL_WRITEHI |107107+ BCM43xx_PIO_TXCTL_COMPLETE);108108+ } else {109109+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,110110+ BCM43xx_PIO_TXCTL_COMPLETE);111111+ }112112+}113113+114114+static u16 generate_cookie(struct bcm43xx_pioqueue *queue,115115+ int packetindex)116116+{117117+ u16 cookie = 0x0000;118118+119119+ /* We use the upper 4 bits for the PIO120120+ * controller ID and the lower 12 bits121121+ * for the packet index (in the cache).122122+ */123123+ switch (queue->mmio_base) {124124+ case BCM43xx_MMIO_PIO1_BASE:125125+ break;126126+ case BCM43xx_MMIO_PIO2_BASE:127127+ cookie = 0x1000;128128+ break;129129+ case BCM43xx_MMIO_PIO3_BASE:130130+ cookie = 0x2000;131131+ break;132132+ case BCM43xx_MMIO_PIO4_BASE:133133+ cookie = 0x3000;134134+ break;135135+ default:136136+ assert(0);137137+ }138138+ assert(((u16)packetindex & 0xF000) == 0x0000);139139+ cookie |= (u16)packetindex;140140+141141+ return cookie;142142+}143143+144144+static145145+struct bcm43xx_pioqueue * parse_cookie(struct bcm43xx_private *bcm,146146+ u16 cookie,147147+ struct bcm43xx_pio_txpacket **packet)148148+{149149+ struct bcm43xx_pio *pio = bcm43xx_current_pio(bcm);150150+ struct bcm43xx_pioqueue *queue = NULL;151151+ int packetindex;152152+153153+ switch (cookie & 0xF000) {154154+ case 0x0000:155155+ queue = pio->queue0;156156+ break;157157+ case 0x1000:158158+ queue = pio->queue1;159159+ break;160160+ case 0x2000:161161+ queue = pio->queue2;162162+ break;163163+ case 0x3000:164164+ queue = pio->queue3;165165+ break;166166+ default:167167+ assert(0);168168+ }169169+ packetindex = (cookie & 0x0FFF);170170+ assert(packetindex >= 0 && packetindex < BCM43xx_PIO_MAXTXPACKETS);171171+ *packet = &(queue->tx_packets_cache[packetindex]);172172+173173+ return queue;174174+}175175+176176+static void pio_tx_write_fragment(struct bcm43xx_pioqueue *queue,177177+ struct sk_buff *skb,178178+ struct bcm43xx_pio_txpacket *packet)179179+{180180+ struct bcm43xx_txhdr txhdr;181181+ unsigned int octets;182182+183183+ assert(skb_shinfo(skb)->nr_frags == 0);184184+ bcm43xx_generate_txhdr(queue->bcm,185185+ &txhdr, skb->data, skb->len,186186+ (packet->xmitted_frags == 0),187187+ generate_cookie(queue, pio_txpacket_getindex(packet)));188188+189189+ tx_start(queue);190190+ octets = skb->len + sizeof(txhdr);191191+ if (queue->need_workarounds)192192+ octets--;193193+ tx_data(queue, &txhdr, (u8 *)skb->data, octets);194194+ tx_complete(queue, skb);195195+}196196+197197+static void free_txpacket(struct bcm43xx_pio_txpacket *packet,198198+ int irq_context)199199+{200200+ struct bcm43xx_pioqueue *queue = packet->queue;201201+202202+ ieee80211_txb_free(packet->txb);203203+ list_move(&packet->list, &queue->txfree);204204+ queue->nr_txfree++;205205+206206+ assert(queue->tx_devq_used >= packet->xmitted_octets);207207+ assert(queue->tx_devq_packets >= packet->xmitted_frags);208208+ queue->tx_devq_used -= packet->xmitted_octets;209209+ queue->tx_devq_packets -= packet->xmitted_frags;210210+}211211+212212+static int pio_tx_packet(struct bcm43xx_pio_txpacket *packet)213213+{214214+ struct bcm43xx_pioqueue *queue = packet->queue;215215+ struct ieee80211_txb *txb = packet->txb;216216+ struct sk_buff *skb;217217+ u16 octets;218218+ int i;219219+220220+ for (i = packet->xmitted_frags; i < txb->nr_frags; i++) {221221+ skb = txb->fragments[i];222222+223223+ octets = (u16)skb->len + sizeof(struct bcm43xx_txhdr);224224+ assert(queue->tx_devq_size >= octets);225225+ assert(queue->tx_devq_packets <= BCM43xx_PIO_MAXTXDEVQPACKETS);226226+ assert(queue->tx_devq_used <= queue->tx_devq_size);227227+ /* Check if there is sufficient free space on the device228228+ * TX queue. If not, return and let the TX tasklet229229+ * retry later.230230+ */231231+ if (queue->tx_devq_packets == BCM43xx_PIO_MAXTXDEVQPACKETS)232232+ return -EBUSY;233233+ if (queue->tx_devq_used + octets > queue->tx_devq_size)234234+ return -EBUSY;235235+ /* Now poke the device. */236236+ pio_tx_write_fragment(queue, skb, packet);237237+238238+ /* Account for the packet size.239239+ * (We must not overflow the device TX queue)240240+ */241241+ queue->tx_devq_packets++;242242+ queue->tx_devq_used += octets;243243+244244+ assert(packet->xmitted_frags <= packet->txb->nr_frags);245245+ packet->xmitted_frags++;246246+ packet->xmitted_octets += octets;247247+ }248248+ list_move_tail(&packet->list, &queue->txrunning);249249+250250+ return 0;251251+}252252+253253+static void tx_tasklet(unsigned long d)254254+{255255+ struct bcm43xx_pioqueue *queue = (struct bcm43xx_pioqueue *)d;256256+ struct bcm43xx_private *bcm = queue->bcm;257257+ unsigned long flags;258258+ struct bcm43xx_pio_txpacket *packet, *tmp_packet;259259+ int err;260260+261261+ bcm43xx_lock_mmio(bcm, flags);262262+ list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) {263263+ assert(packet->xmitted_frags < packet->txb->nr_frags);264264+ if (packet->xmitted_frags == 0) {265265+ int i;266266+ struct sk_buff *skb;267267+268268+ /* Check if the device queue is big269269+ * enough for every fragment. If not, drop the270270+ * whole packet.271271+ */272272+ for (i = 0; i < packet->txb->nr_frags; i++) {273273+ skb = packet->txb->fragments[i];274274+ if (unlikely(skb->len > queue->tx_devq_size)) {275275+ dprintkl(KERN_ERR PFX "PIO TX device queue too small. "276276+ "Dropping packet.\n");277277+ free_txpacket(packet, 1);278278+ goto next_packet;279279+ }280280+ }281281+ }282282+ /* Try to transmit the packet.283283+ * This may not completely succeed.284284+ */285285+ err = pio_tx_packet(packet);286286+ if (err)287287+ break;288288+ next_packet:289289+ continue;290290+ }291291+ bcm43xx_unlock_mmio(bcm, flags);292292+}293293+294294+static void setup_txqueues(struct bcm43xx_pioqueue *queue)295295+{296296+ struct bcm43xx_pio_txpacket *packet;297297+ int i;298298+299299+ queue->nr_txfree = BCM43xx_PIO_MAXTXPACKETS;300300+ for (i = 0; i < BCM43xx_PIO_MAXTXPACKETS; i++) {301301+ packet = &(queue->tx_packets_cache[i]);302302+303303+ packet->queue = queue;304304+ INIT_LIST_HEAD(&packet->list);305305+306306+ list_add(&packet->list, &queue->txfree);307307+ }308308+}309309+310310+static311311+struct bcm43xx_pioqueue * bcm43xx_setup_pioqueue(struct bcm43xx_private *bcm,312312+ u16 pio_mmio_base)313313+{314314+ struct bcm43xx_pioqueue *queue;315315+ u32 value;316316+ u16 qsize;317317+318318+ queue = kzalloc(sizeof(*queue), GFP_KERNEL);319319+ if (!queue)320320+ goto out;321321+322322+ queue->bcm = bcm;323323+ queue->mmio_base = pio_mmio_base;324324+ queue->need_workarounds = (bcm->current_core->rev < 3);325325+326326+ INIT_LIST_HEAD(&queue->txfree);327327+ INIT_LIST_HEAD(&queue->txqueue);328328+ INIT_LIST_HEAD(&queue->txrunning);329329+ tasklet_init(&queue->txtask, tx_tasklet,330330+ (unsigned long)queue);331331+332332+ value = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);333333+ value |= BCM43xx_SBF_XFER_REG_BYTESWAP;334334+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value);335335+336336+ qsize = bcm43xx_read16(bcm, queue->mmio_base + BCM43xx_PIO_TXQBUFSIZE);337337+ if (qsize <= BCM43xx_PIO_TXQADJUST) {338338+ printk(KERN_ERR PFX "PIO tx device-queue too small (%u)\n", qsize);339339+ goto err_freequeue;340340+ }341341+ qsize -= BCM43xx_PIO_TXQADJUST;342342+ queue->tx_devq_size = qsize;343343+344344+ setup_txqueues(queue);345345+346346+out:347347+ return queue;348348+349349+err_freequeue:350350+ kfree(queue);351351+ queue = NULL;352352+ goto out;353353+}354354+355355+static void cancel_transfers(struct bcm43xx_pioqueue *queue)356356+{357357+ struct bcm43xx_pio_txpacket *packet, *tmp_packet;358358+359359+ netif_tx_disable(queue->bcm->net_dev);360360+ assert(queue->bcm->shutting_down);361361+ tasklet_disable(&queue->txtask);362362+363363+ list_for_each_entry_safe(packet, tmp_packet, &queue->txrunning, list)364364+ free_txpacket(packet, 0);365365+ list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list)366366+ free_txpacket(packet, 0);367367+}368368+369369+static void bcm43xx_destroy_pioqueue(struct bcm43xx_pioqueue *queue)370370+{371371+ if (!queue)372372+ return;373373+374374+ cancel_transfers(queue);375375+ kfree(queue);376376+}377377+378378+void bcm43xx_pio_free(struct bcm43xx_private *bcm)379379+{380380+ struct bcm43xx_pio *pio;381381+382382+ if (!bcm43xx_using_pio(bcm))383383+ return;384384+ pio = bcm43xx_current_pio(bcm);385385+386386+ bcm43xx_destroy_pioqueue(pio->queue3);387387+ pio->queue3 = NULL;388388+ bcm43xx_destroy_pioqueue(pio->queue2);389389+ pio->queue2 = NULL;390390+ bcm43xx_destroy_pioqueue(pio->queue1);391391+ pio->queue1 = NULL;392392+ bcm43xx_destroy_pioqueue(pio->queue0);393393+ pio->queue0 = NULL;394394+}395395+396396+int bcm43xx_pio_init(struct bcm43xx_private *bcm)397397+{398398+ struct bcm43xx_pio *pio = bcm43xx_current_pio(bcm);399399+ struct bcm43xx_pioqueue *queue;400400+ int err = -ENOMEM;401401+402402+ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO1_BASE);403403+ if (!queue)404404+ goto out;405405+ pio->queue0 = queue;406406+407407+ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO2_BASE);408408+ if (!queue)409409+ goto err_destroy0;410410+ pio->queue1 = queue;411411+412412+ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO3_BASE);413413+ if (!queue)414414+ goto err_destroy1;415415+ pio->queue2 = queue;416416+417417+ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO4_BASE);418418+ if (!queue)419419+ goto err_destroy2;420420+ pio->queue3 = queue;421421+422422+ if (bcm->current_core->rev < 3)423423+ bcm->irq_savedstate |= BCM43xx_IRQ_PIO_WORKAROUND;424424+425425+ dprintk(KERN_INFO PFX "PIO initialized\n");426426+ err = 0;427427+out:428428+ return err;429429+430430+err_destroy2:431431+ bcm43xx_destroy_pioqueue(pio->queue2);432432+ pio->queue2 = NULL;433433+err_destroy1:434434+ bcm43xx_destroy_pioqueue(pio->queue1);435435+ pio->queue1 = NULL;436436+err_destroy0:437437+ bcm43xx_destroy_pioqueue(pio->queue0);438438+ pio->queue0 = NULL;439439+ goto out;440440+}441441+442442+int bcm43xx_pio_tx(struct bcm43xx_private *bcm,443443+ struct ieee80211_txb *txb)444444+{445445+ struct bcm43xx_pioqueue *queue = bcm43xx_current_pio(bcm)->queue1;446446+ struct bcm43xx_pio_txpacket *packet;447447+ u16 tmp;448448+449449+ assert(!queue->tx_suspended);450450+ assert(!list_empty(&queue->txfree));451451+452452+ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL);453453+ if (tmp & BCM43xx_PIO_TXCTL_SUSPEND)454454+ return -EBUSY;455455+456456+ packet = list_entry(queue->txfree.next, struct bcm43xx_pio_txpacket, list);457457+ packet->txb = txb;458458+ packet->xmitted_frags = 0;459459+ packet->xmitted_octets = 0;460460+ list_move_tail(&packet->list, &queue->txqueue);461461+ queue->nr_txfree--;462462+ assert(queue->nr_txfree < BCM43xx_PIO_MAXTXPACKETS);463463+464464+ /* Suspend TX, if we are out of packets in the "free" queue. */465465+ if (unlikely(list_empty(&queue->txfree))) {466466+ netif_stop_queue(queue->bcm->net_dev);467467+ queue->tx_suspended = 1;468468+ }469469+470470+ tasklet_schedule(&queue->txtask);471471+472472+ return 0;473473+}474474+475475+void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm,476476+ struct bcm43xx_xmitstatus *status)477477+{478478+ struct bcm43xx_pioqueue *queue;479479+ struct bcm43xx_pio_txpacket *packet;480480+481481+ queue = parse_cookie(bcm, status->cookie, &packet);482482+ assert(queue);483483+//TODO484484+if (!queue)485485+return;486486+ free_txpacket(packet, 1);487487+ if (unlikely(queue->tx_suspended)) {488488+ queue->tx_suspended = 0;489489+ netif_wake_queue(queue->bcm->net_dev);490490+ }491491+ /* If there are packets on the txqueue, poke the tasklet. */492492+ if (!list_empty(&queue->txqueue))493493+ tasklet_schedule(&queue->txtask);494494+}495495+496496+static void pio_rx_error(struct bcm43xx_pioqueue *queue,497497+ int clear_buffers,498498+ const char *error)499499+{500500+ int i;501501+502502+ printkl("PIO RX error: %s\n", error);503503+ bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL,504504+ BCM43xx_PIO_RXCTL_READY);505505+ if (clear_buffers) {506506+ assert(queue->mmio_base == BCM43xx_MMIO_PIO1_BASE);507507+ for (i = 0; i < 15; i++) {508508+ /* Dummy read. */509509+ bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA);510510+ }511511+ }512512+}513513+514514+void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue)515515+{516516+ u16 preamble[21] = { 0 };517517+ struct bcm43xx_rxhdr *rxhdr;518518+ u16 tmp, len, rxflags2;519519+ int i, preamble_readwords;520520+ struct sk_buff *skb;521521+522522+return;523523+ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL);524524+ if (!(tmp & BCM43xx_PIO_RXCTL_DATAAVAILABLE)) {525525+ dprintkl(KERN_ERR PFX "PIO RX: No data available\n");//TODO: remove this printk.526526+ return;527527+ }528528+ bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL,529529+ BCM43xx_PIO_RXCTL_DATAAVAILABLE);530530+531531+ for (i = 0; i < 10; i++) {532532+ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL);533533+ if (tmp & BCM43xx_PIO_RXCTL_READY)534534+ goto data_ready;535535+ udelay(10);536536+ }537537+ dprintkl(KERN_ERR PFX "PIO RX timed out\n");538538+ return;539539+data_ready:540540+541541+//FIXME: endianess in this function.542542+ len = le16_to_cpu(bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA));543543+ if (unlikely(len > 0x700)) {544544+ pio_rx_error(queue, 0, "len > 0x700");545545+ return;546546+ }547547+ if (unlikely(len == 0 && queue->mmio_base != BCM43xx_MMIO_PIO4_BASE)) {548548+ pio_rx_error(queue, 0, "len == 0");549549+ return;550550+ }551551+ preamble[0] = cpu_to_le16(len);552552+ if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE)553553+ preamble_readwords = 14 / sizeof(u16);554554+ else555555+ preamble_readwords = 18 / sizeof(u16);556556+ for (i = 0; i < preamble_readwords; i++) {557557+ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA);558558+ preamble[i + 1] = cpu_to_be16(tmp);//FIXME?559559+ }560560+ rxhdr = (struct bcm43xx_rxhdr *)preamble;561561+ rxflags2 = le16_to_cpu(rxhdr->flags2);562562+ if (unlikely(rxflags2 & BCM43xx_RXHDR_FLAGS2_INVALIDFRAME)) {563563+ pio_rx_error(queue,564564+ (queue->mmio_base == BCM43xx_MMIO_PIO1_BASE),565565+ "invalid frame");566566+ return;567567+ }568568+ if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE) {569569+ /* We received an xmit status. */570570+ struct bcm43xx_hwxmitstatus *hw;571571+ struct bcm43xx_xmitstatus stat;572572+573573+ hw = (struct bcm43xx_hwxmitstatus *)(preamble + 1);574574+ stat.cookie = le16_to_cpu(hw->cookie);575575+ stat.flags = hw->flags;576576+ stat.cnt1 = hw->cnt1;577577+ stat.cnt2 = hw->cnt2;578578+ stat.seq = le16_to_cpu(hw->seq);579579+ stat.unknown = le16_to_cpu(hw->unknown);580580+581581+ bcm43xx_debugfs_log_txstat(queue->bcm, &stat);582582+ bcm43xx_pio_handle_xmitstatus(queue->bcm, &stat);583583+584584+ return;585585+ }586586+587587+ skb = dev_alloc_skb(len);588588+ if (unlikely(!skb)) {589589+ pio_rx_error(queue, 1, "OOM");590590+ return;591591+ }592592+ skb_put(skb, len);593593+ for (i = 0; i < len - 1; i += 2) {594594+ tmp = cpu_to_be16(bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA));595595+ *((u16 *)(skb->data + i)) = tmp;596596+ }597597+ if (len % 2) {598598+ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA);599599+ skb->data[len - 1] = (tmp & 0x00FF);600600+ if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME)601601+ skb->data[0x20] = (tmp & 0xFF00) >> 8;602602+ else603603+ skb->data[0x1E] = (tmp & 0xFF00) >> 8;604604+ }605605+ bcm43xx_rx(queue->bcm, skb, rxhdr);606606+}
+138
drivers/net/wireless/bcm43xx/bcm43xx_pio.h
···11+#ifndef BCM43xx_PIO_H_22+#define BCM43xx_PIO_H_33+44+#include "bcm43xx.h"55+66+#include <linux/interrupt.h>77+#include <linux/list.h>88+#include <linux/skbuff.h>99+1010+1111+#define BCM43xx_PIO_TXCTL 0x001212+#define BCM43xx_PIO_TXDATA 0x021313+#define BCM43xx_PIO_TXQBUFSIZE 0x041414+#define BCM43xx_PIO_RXCTL 0x081515+#define BCM43xx_PIO_RXDATA 0x0A1616+1717+#define BCM43xx_PIO_TXCTL_WRITEHI (1 << 0)1818+#define BCM43xx_PIO_TXCTL_WRITELO (1 << 1)1919+#define BCM43xx_PIO_TXCTL_COMPLETE (1 << 2)2020+#define BCM43xx_PIO_TXCTL_INIT (1 << 3)2121+#define BCM43xx_PIO_TXCTL_SUSPEND (1 << 7)2222+2323+#define BCM43xx_PIO_RXCTL_DATAAVAILABLE (1 << 0)2424+#define BCM43xx_PIO_RXCTL_READY (1 << 1)2525+2626+/* PIO constants */2727+#define BCM43xx_PIO_MAXTXDEVQPACKETS 312828+#define BCM43xx_PIO_TXQADJUST 802929+3030+/* PIO tuning knobs */3131+#define BCM43xx_PIO_MAXTXPACKETS 2563232+3333+3434+3535+#ifdef CONFIG_BCM43XX_PIO3636+3737+3838+struct bcm43xx_pioqueue;3939+struct bcm43xx_xmitstatus;4040+4141+struct bcm43xx_pio_txpacket {4242+ struct bcm43xx_pioqueue *queue;4343+ struct ieee80211_txb *txb;4444+ struct list_head list;4545+4646+ u8 xmitted_frags;4747+ u16 xmitted_octets;4848+};4949+5050+#define pio_txpacket_getindex(packet) ((int)((packet) - (packet)->queue->tx_packets_cache)) 5151+5252+struct bcm43xx_pioqueue {5353+ struct bcm43xx_private *bcm;5454+ u16 mmio_base;5555+5656+ u8 tx_suspended:1,5757+ need_workarounds:1; /* Workarounds needed for core.rev < 3 */5858+5959+ /* Adjusted size of the device internal TX buffer. */6060+ u16 tx_devq_size;6161+ /* Used octets of the device internal TX buffer. */6262+ u16 tx_devq_used;6363+ /* Used packet slots in the device internal TX buffer. */6464+ u8 tx_devq_packets;6565+ /* Packets from the txfree list can6666+ * be taken on incoming TX requests.6767+ */6868+ struct list_head txfree;6969+ unsigned int nr_txfree;7070+ /* Packets on the txqueue are queued,7171+ * but not completely written to the chip, yet.7272+ */7373+ struct list_head txqueue;7474+ /* Packets on the txrunning queue are completely7575+ * posted to the device. We are waiting for the txstatus.7676+ */7777+ struct list_head txrunning;7878+ /* Total number or packets sent.7979+ * (This counter can obviously wrap).8080+ */8181+ unsigned int nr_tx_packets;8282+ struct tasklet_struct txtask;8383+ struct bcm43xx_pio_txpacket tx_packets_cache[BCM43xx_PIO_MAXTXPACKETS];8484+};8585+8686+static inline8787+u16 bcm43xx_pio_read(struct bcm43xx_pioqueue *queue,8888+ u16 offset)8989+{9090+ return bcm43xx_read16(queue->bcm, queue->mmio_base + offset);9191+}9292+9393+static inline9494+void bcm43xx_pio_write(struct bcm43xx_pioqueue *queue,9595+ u16 offset, u16 value)9696+{9797+ bcm43xx_write16(queue->bcm, queue->mmio_base + offset, value);9898+}9999+100100+101101+int bcm43xx_pio_init(struct bcm43xx_private *bcm);102102+void bcm43xx_pio_free(struct bcm43xx_private *bcm);103103+104104+int bcm43xx_pio_tx(struct bcm43xx_private *bcm,105105+ struct ieee80211_txb *txb);106106+void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm,107107+ struct bcm43xx_xmitstatus *status);108108+void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue);109109+110110+#else /* CONFIG_BCM43XX_PIO */111111+112112+static inline113113+int bcm43xx_pio_init(struct bcm43xx_private *bcm)114114+{115115+ return 0;116116+}117117+static inline118118+void bcm43xx_pio_free(struct bcm43xx_private *bcm)119119+{120120+}121121+static inline122122+int bcm43xx_pio_tx(struct bcm43xx_private *bcm,123123+ struct ieee80211_txb *txb)124124+{125125+ return 0;126126+}127127+static inline128128+void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm,129129+ struct bcm43xx_xmitstatus *status)130130+{131131+}132132+static inline133133+void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue)134134+{135135+}136136+137137+#endif /* CONFIG_BCM43XX_PIO */138138+#endif /* BCM43xx_PIO_H_ */
+358
drivers/net/wireless/bcm43xx/bcm43xx_power.c
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,66+ Stefano Brivio <st3@riseup.net>77+ Michael Buesch <mbuesch@freenet.de>88+ Danny van Dyk <kugelfang@gentoo.org>99+ Andreas Jaggi <andreas.jaggi@waterwave.ch>1010+1111+ Some parts of the code in this file are derived from the ipw22001212+ driver Copyright(c) 2003 - 2004 Intel Corporation.1313+1414+ This program is free software; you can redistribute it and/or modify1515+ it under the terms of the GNU General Public License as published by1616+ the Free Software Foundation; either version 2 of the License, or1717+ (at your option) any later version.1818+1919+ This program is distributed in the hope that it will be useful,2020+ but WITHOUT ANY WARRANTY; without even the implied warranty of2121+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the2222+ GNU General Public License for more details.2323+2424+ You should have received a copy of the GNU General Public License2525+ along with this program; see the file COPYING. If not, write to2626+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2727+ Boston, MA 02110-1301, USA.2828+2929+*/3030+3131+#include <linux/delay.h>3232+3333+#include "bcm43xx.h"3434+#include "bcm43xx_power.h"3535+#include "bcm43xx_main.h"3636+3737+3838+/* Get max/min slowclock frequency3939+ * as described in http://bcm-specs.sipsolutions.net/PowerControl4040+ */4141+static int bcm43xx_pctl_clockfreqlimit(struct bcm43xx_private *bcm,4242+ int get_max)4343+{4444+ int limit = 0;4545+ int divisor;4646+ int selection;4747+ int err;4848+ u32 tmp;4949+ struct bcm43xx_coreinfo *old_core;5050+5151+ if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))5252+ goto out;5353+ old_core = bcm->current_core;5454+ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);5555+ if (err)5656+ goto out;5757+5858+ if (bcm->current_core->rev < 6) {5959+ if ((bcm->bustype == BCM43xx_BUSTYPE_PCMCIA) ||6060+ (bcm->bustype == BCM43xx_BUSTYPE_SB)) {6161+ selection = 1;6262+ divisor = 32;6363+ } else {6464+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &tmp);6565+ if (err) {6666+ printk(KERN_ERR PFX "clockfreqlimit pcicfg read failure\n");6767+ goto out_switchback;6868+ }6969+ if (tmp & 0x10) {7070+ /* PCI */7171+ selection = 2;7272+ divisor = 64;7373+ } else {7474+ /* XTAL */7575+ selection = 1;7676+ divisor = 32;7777+ }7878+ }7979+ } else if (bcm->current_core->rev < 10) {8080+ selection = (tmp & 0x07);8181+ if (selection) {8282+ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);8383+ divisor = 4 * (1 + ((tmp & 0xFFFF0000) >> 16));8484+ } else8585+ divisor = 1;8686+ } else {8787+ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL);8888+ divisor = 4 * (1 + ((tmp & 0xFFFF0000) >> 16));8989+ selection = 1;9090+ }9191+9292+ switch (selection) {9393+ case 0:9494+ /* LPO */9595+ if (get_max)9696+ limit = 43000;9797+ else9898+ limit = 25000;9999+ break;100100+ case 1:101101+ /* XTAL */102102+ if (get_max)103103+ limit = 20200000;104104+ else105105+ limit = 19800000;106106+ break;107107+ case 2:108108+ /* PCI */109109+ if (get_max)110110+ limit = 34000000;111111+ else112112+ limit = 25000000;113113+ break;114114+ default:115115+ assert(0);116116+ }117117+ limit /= divisor;118118+119119+out_switchback:120120+ err = bcm43xx_switch_core(bcm, old_core);121121+ assert(err == 0);122122+123123+out:124124+ return limit;125125+}126126+127127+/* init power control128128+ * as described in http://bcm-specs.sipsolutions.net/PowerControl129129+ */130130+int bcm43xx_pctl_init(struct bcm43xx_private *bcm)131131+{132132+ int err, maxfreq;133133+ struct bcm43xx_coreinfo *old_core;134134+135135+ if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))136136+ return 0;137137+ old_core = bcm->current_core;138138+ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);139139+ if (err == -ENODEV)140140+ return 0;141141+ if (err)142142+ goto out;143143+144144+ maxfreq = bcm43xx_pctl_clockfreqlimit(bcm, 1);145145+ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY,146146+ (maxfreq * 150 + 999999) / 1000000);147147+ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_FREFSELDELAY,148148+ (maxfreq * 15 + 999999) / 1000000);149149+150150+ err = bcm43xx_switch_core(bcm, old_core);151151+ assert(err == 0);152152+153153+out:154154+ return err;155155+}156156+157157+u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm)158158+{159159+ u16 delay = 0;160160+ int err;161161+ u32 pll_on_delay;162162+ struct bcm43xx_coreinfo *old_core;163163+ int minfreq;164164+165165+ if (bcm->bustype != BCM43xx_BUSTYPE_PCI)166166+ goto out;167167+ if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))168168+ goto out;169169+ old_core = bcm->current_core;170170+ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);171171+ if (err == -ENODEV)172172+ goto out;173173+174174+ minfreq = bcm43xx_pctl_clockfreqlimit(bcm, 0);175175+ pll_on_delay = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY);176176+ delay = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq;177177+178178+ err = bcm43xx_switch_core(bcm, old_core);179179+ assert(err == 0);180180+181181+out:182182+ return delay;183183+}184184+185185+/* set the powercontrol clock186186+ * as described in http://bcm-specs.sipsolutions.net/PowerControl187187+ */188188+int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode)189189+{190190+ int err;191191+ struct bcm43xx_coreinfo *old_core;192192+ u32 tmp;193193+194194+ old_core = bcm->current_core;195195+ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);196196+ if (err == -ENODEV)197197+ return 0;198198+ if (err)199199+ goto out;200200+201201+ if (bcm->core_chipcommon.rev < 6) {202202+ if (mode == BCM43xx_PCTL_CLK_FAST) {203203+ err = bcm43xx_pctl_set_crystal(bcm, 1);204204+ if (err)205205+ goto out;206206+ }207207+ } else {208208+ if ((bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) &&209209+ (bcm->core_chipcommon.rev < 10)) {210210+ switch (mode) {211211+ case BCM43xx_PCTL_CLK_FAST:212212+ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);213213+ tmp = (tmp & ~BCM43xx_PCTL_FORCE_SLOW) | BCM43xx_PCTL_FORCE_PLL;214214+ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);215215+ break;216216+ case BCM43xx_PCTL_CLK_SLOW:217217+ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);218218+ tmp |= BCM43xx_PCTL_FORCE_SLOW;219219+ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);220220+ break;221221+ case BCM43xx_PCTL_CLK_DYNAMIC:222222+ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);223223+ tmp &= ~BCM43xx_PCTL_FORCE_SLOW;224224+ tmp |= BCM43xx_PCTL_FORCE_PLL;225225+ tmp &= ~BCM43xx_PCTL_DYN_XTAL;226226+ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);227227+ }228228+ }229229+ }230230+231231+ err = bcm43xx_switch_core(bcm, old_core);232232+ assert(err == 0);233233+234234+out:235235+ return err;236236+}237237+238238+int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on)239239+{240240+ int err;241241+ u32 in, out, outenable;242242+243243+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_IN, &in);244244+ if (err)245245+ goto err_pci;246246+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &out);247247+ if (err)248248+ goto err_pci;249249+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUTENABLE, &outenable);250250+ if (err)251251+ goto err_pci;252252+253253+ outenable |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);254254+255255+ if (on) {256256+ if (in & 0x40)257257+ return 0;258258+259259+ out |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);260260+261261+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);262262+ if (err)263263+ goto err_pci;264264+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);265265+ if (err)266266+ goto err_pci;267267+ udelay(1000);268268+269269+ out &= ~BCM43xx_PCTL_PLL_POWERDOWN;270270+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);271271+ if (err)272272+ goto err_pci;273273+ udelay(5000);274274+ } else {275275+ if (bcm->current_core->rev < 5)276276+ return 0;277277+ if (bcm->sprom.boardflags & BCM43xx_BFL_XTAL_NOSLOW)278278+ return 0;279279+280280+/* XXX: Why BCM43xx_MMIO_RADIO_HWENABLED_xx can't be read at this time?281281+ * err = bcm43xx_switch_core(bcm, bcm->active_80211_core);282282+ * if (err)283283+ * return err;284284+ * if (((bcm->current_core->rev >= 3) &&285285+ * (bcm43xx_read32(bcm, BCM43xx_MMIO_RADIO_HWENABLED_HI) & (1 << 16))) ||286286+ * ((bcm->current_core->rev < 3) &&287287+ * !(bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_HWENABLED_LO) & (1 << 4))))288288+ * return 0;289289+ * err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);290290+ * if (err)291291+ * return err;292292+ */293293+294294+ err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW);295295+ if (err)296296+ goto out;297297+ out &= ~BCM43xx_PCTL_XTAL_POWERUP;298298+ out |= BCM43xx_PCTL_PLL_POWERDOWN;299299+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);300300+ if (err)301301+ goto err_pci;302302+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);303303+ if (err)304304+ goto err_pci;305305+ }306306+307307+out:308308+ return err;309309+310310+err_pci:311311+ printk(KERN_ERR PFX "Error: pctl_set_clock() could not access PCI config space!\n");312312+ err = -EBUSY;313313+ goto out;314314+}315315+316316+/* Set the PowerSavingControlBits.317317+ * Bitvalues:318318+ * 0 => unset the bit319319+ * 1 => set the bit320320+ * -1 => calculate the bit321321+ */322322+void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm,323323+ int bit25, int bit26)324324+{325325+ int i;326326+ u32 status;327327+328328+//FIXME: Force 25 to off and 26 to on for now:329329+bit25 = 0;330330+bit26 = 1;331331+332332+ if (bit25 == -1) {333333+ //TODO: If powersave is not off and FIXME is not set and we are not in adhoc334334+ // and thus is not an AP and we are associated, set bit 25335335+ }336336+ if (bit26 == -1) {337337+ //TODO: If the device is awake or this is an AP, or we are scanning, or FIXME,338338+ // or we are associated, or FIXME, or the latest PS-Poll packet sent was339339+ // successful, set bit26340340+ }341341+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);342342+ if (bit25)343343+ status |= BCM43xx_SBF_PS1;344344+ else345345+ status &= ~BCM43xx_SBF_PS1;346346+ if (bit26)347347+ status |= BCM43xx_SBF_PS2;348348+ else349349+ status &= ~BCM43xx_SBF_PS2;350350+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);351351+ if (bit26 && bcm->current_core->rev >= 5) {352352+ for (i = 0; i < 100; i++) {353353+ if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0040) != 4)354354+ break;355355+ udelay(10);356356+ }357357+ }358358+}
+47
drivers/net/wireless/bcm43xx/bcm43xx_power.h
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,66+ Stefano Brivio <st3@riseup.net>77+ Michael Buesch <mbuesch@freenet.de>88+ Danny van Dyk <kugelfang@gentoo.org>99+ Andreas Jaggi <andreas.jaggi@waterwave.ch>1010+1111+ Some parts of the code in this file are derived from the ipw22001212+ driver Copyright(c) 2003 - 2004 Intel Corporation.1313+1414+ This program is free software; you can redistribute it and/or modify1515+ it under the terms of the GNU General Public License as published by1616+ the Free Software Foundation; either version 2 of the License, or1717+ (at your option) any later version.1818+1919+ This program is distributed in the hope that it will be useful,2020+ but WITHOUT ANY WARRANTY; without even the implied warranty of2121+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the2222+ GNU General Public License for more details.2323+2424+ You should have received a copy of the GNU General Public License2525+ along with this program; see the file COPYING. If not, write to2626+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2727+ Boston, MA 02110-1301, USA.2828+2929+*/3030+3131+#ifndef BCM43xx_POWER_H_3232+#define BCM43xx_POWER_H_3333+3434+#include <linux/types.h>3535+3636+3737+struct bcm43xx_private;3838+3939+int bcm43xx_pctl_init(struct bcm43xx_private *bcm);4040+int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode);4141+int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on);4242+u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm);4343+4444+void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm,4545+ int bit25, int bit26);4646+4747+#endif /* BCM43xx_POWER_H_ */
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,66+ Stefano Brivio <st3@riseup.net>77+ Michael Buesch <mbuesch@freenet.de>88+ Danny van Dyk <kugelfang@gentoo.org>99+ Andreas Jaggi <andreas.jaggi@waterwave.ch>1010+1111+ Some parts of the code in this file are derived from the ipw22001212+ driver Copyright(c) 2003 - 2004 Intel Corporation.1313+1414+ This program is free software; you can redistribute it and/or modify1515+ it under the terms of the GNU General Public License as published by1616+ the Free Software Foundation; either version 2 of the License, or1717+ (at your option) any later version.1818+1919+ This program is distributed in the hope that it will be useful,2020+ but WITHOUT ANY WARRANTY; without even the implied warranty of2121+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the2222+ GNU General Public License for more details.2323+2424+ You should have received a copy of the GNU General Public License2525+ along with this program; see the file COPYING. If not, write to2626+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2727+ Boston, MA 02110-1301, USA.2828+2929+*/3030+3131+#ifndef BCM43xx_RADIO_H_3232+#define BCM43xx_RADIO_H_3333+3434+#include "bcm43xx.h"3535+3636+3737+#define BCM43xx_RADIO_DEFAULT_CHANNEL_A 363838+#define BCM43xx_RADIO_DEFAULT_CHANNEL_BG 63939+4040+/* Force antenna 0. */4141+#define BCM43xx_RADIO_TXANTENNA_0 04242+/* Force antenna 1. */4343+#define BCM43xx_RADIO_TXANTENNA_1 14444+/* Use the RX antenna, that was selected for the most recently4545+ * received good PLCP header.4646+ */4747+#define BCM43xx_RADIO_TXANTENNA_LASTPLCP 34848+#define BCM43xx_RADIO_TXANTENNA_DEFAULT BCM43xx_RADIO_TXANTENNA_LASTPLCP4949+5050+#define BCM43xx_RADIO_INTERFMODE_NONE 05151+#define BCM43xx_RADIO_INTERFMODE_NONWLAN 15252+#define BCM43xx_RADIO_INTERFMODE_MANUALWLAN 25353+#define BCM43xx_RADIO_INTERFMODE_AUTOWLAN 35454+5555+5656+void bcm43xx_radio_lock(struct bcm43xx_private *bcm);5757+void bcm43xx_radio_unlock(struct bcm43xx_private *bcm);5858+5959+u16 bcm43xx_radio_read16(struct bcm43xx_private *bcm, u16 offset);6060+void bcm43xx_radio_write16(struct bcm43xx_private *bcm, u16 offset, u16 val);6161+6262+u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm);6363+void bcm43xx_radio_init2060(struct bcm43xx_private *bcm);6464+6565+void bcm43xx_radio_turn_on(struct bcm43xx_private *bcm);6666+void bcm43xx_radio_turn_off(struct bcm43xx_private *bcm);6767+6868+int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm, u8 channel,6969+ int synthetic_pu_workaround);7070+7171+void bcm43xx_radio_set_txpower_a(struct bcm43xx_private *bcm, u16 txpower);7272+void bcm43xx_radio_set_txpower_bg(struct bcm43xx_private *bcm,7373+ u16 baseband_attenuation, u16 attenuation,7474+ u16 txpower);7575+7676+u16 bcm43xx_default_baseband_attenuation(struct bcm43xx_private *bcm);7777+u16 bcm43xx_default_radio_attenuation(struct bcm43xx_private *bcm);7878+u16 bcm43xx_default_txctl1(struct bcm43xx_private *bcm);7979+8080+void bcm43xx_radio_set_txantenna(struct bcm43xx_private *bcm, u32 val);8181+8282+void bcm43xx_radio_clear_tssi(struct bcm43xx_private *bcm);8383+8484+u8 bcm43xx_radio_aci_detect(struct bcm43xx_private *bcm, u8 channel);8585+u8 bcm43xx_radio_aci_scan(struct bcm43xx_private *bcm);8686+8787+int bcm43xx_radio_set_interference_mitigation(struct bcm43xx_private *bcm, int mode);8888+8989+void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm);9090+void bcm43xx_calc_nrssi_threshold(struct bcm43xx_private *bcm);9191+s16 bcm43xx_nrssi_hw_read(struct bcm43xx_private *bcm, u16 offset);9292+void bcm43xx_nrssi_hw_write(struct bcm43xx_private *bcm, u16 offset, s16 val);9393+void bcm43xx_nrssi_hw_update(struct bcm43xx_private *bcm, u16 val);9494+void bcm43xx_nrssi_mem_update(struct bcm43xx_private *bcm);9595+9696+void bcm43xx_radio_set_tx_iq(struct bcm43xx_private *bcm);9797+u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_private *bcm);9898+9999+#endif /* BCM43xx_RADIO_H_ */
+322
drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ SYSFS support routines66+77+ Copyright (c) 2006 Michael Buesch <mbuesch@freenet.de>88+99+ This program is free software; you can redistribute it and/or modify1010+ it under the terms of the GNU General Public License as published by1111+ the Free Software Foundation; either version 2 of the License, or1212+ (at your option) any later version.1313+1414+ This program is distributed in the hope that it will be useful,1515+ but WITHOUT ANY WARRANTY; without even the implied warranty of1616+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1717+ GNU General Public License for more details.1818+1919+ You should have received a copy of the GNU General Public License2020+ along with this program; see the file COPYING. If not, write to2121+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2222+ Boston, MA 02110-1301, USA.2323+2424+*/2525+2626+#include "bcm43xx_sysfs.h"2727+#include "bcm43xx.h"2828+#include "bcm43xx_main.h"2929+#include "bcm43xx_radio.h"3030+3131+#include <linux/capability.h>3232+3333+3434+#define GENERIC_FILESIZE 643535+3636+3737+static int get_integer(const char *buf, size_t count)3838+{3939+ char tmp[10 + 1] = { 0 };4040+ int ret = -EINVAL;4141+4242+ if (count == 0)4343+ goto out;4444+ count = min(count, (size_t)10);4545+ memcpy(tmp, buf, count);4646+ ret = simple_strtol(tmp, NULL, 10);4747+out:4848+ return ret;4949+}5050+5151+static int get_boolean(const char *buf, size_t count)5252+{5353+ if (count != 0) {5454+ if (buf[0] == '1')5555+ return 1;5656+ if (buf[0] == '0')5757+ return 0;5858+ if (count >= 4 && memcmp(buf, "true", 4) == 0)5959+ return 1;6060+ if (count >= 5 && memcmp(buf, "false", 5) == 0)6161+ return 0;6262+ if (count >= 3 && memcmp(buf, "yes", 3) == 0)6363+ return 1;6464+ if (count >= 2 && memcmp(buf, "no", 2) == 0)6565+ return 0;6666+ if (count >= 2 && memcmp(buf, "on", 2) == 0)6767+ return 1;6868+ if (count >= 3 && memcmp(buf, "off", 3) == 0)6969+ return 0;7070+ }7171+ return -EINVAL;7272+}7373+7474+static ssize_t bcm43xx_attr_sprom_show(struct device *dev,7575+ struct device_attribute *attr,7676+ char *buf)7777+{7878+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom);7979+ u16 *sprom;8080+ unsigned long flags;8181+ int i, err;8282+8383+ if (!capable(CAP_NET_ADMIN))8484+ return -EPERM;8585+8686+ assert(BCM43xx_SPROM_SIZE * sizeof(u16) <= PAGE_SIZE);8787+ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),8888+ GFP_KERNEL);8989+ if (!sprom)9090+ return -ENOMEM;9191+ bcm43xx_lock_mmio(bcm, flags);9292+ assert(bcm->initialized);9393+ err = bcm43xx_sprom_read(bcm, sprom);9494+ if (!err) {9595+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {9696+ buf[i * 2] = sprom[i] & 0x00FF;9797+ buf[i * 2 + 1] = (sprom[i] & 0xFF00) >> 8;9898+ }9999+ }100100+ bcm43xx_unlock_mmio(bcm, flags);101101+ kfree(sprom);102102+103103+ return err ? err : BCM43xx_SPROM_SIZE * sizeof(u16);104104+}105105+106106+static ssize_t bcm43xx_attr_sprom_store(struct device *dev,107107+ struct device_attribute *attr,108108+ const char *buf, size_t count)109109+{110110+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom);111111+ u16 *sprom;112112+ unsigned long flags;113113+ int i, err;114114+115115+ if (!capable(CAP_NET_ADMIN))116116+ return -EPERM;117117+118118+ if (count != BCM43xx_SPROM_SIZE * sizeof(u16))119119+ return -EINVAL;120120+ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),121121+ GFP_KERNEL);122122+ if (!sprom)123123+ return -ENOMEM;124124+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {125125+ sprom[i] = buf[i * 2] & 0xFF;126126+ sprom[i] |= ((u16)(buf[i * 2 + 1] & 0xFF)) << 8;127127+ }128128+ bcm43xx_lock_mmio(bcm, flags);129129+ assert(bcm->initialized);130130+ err = bcm43xx_sprom_write(bcm, sprom);131131+ bcm43xx_unlock_mmio(bcm, flags);132132+ kfree(sprom);133133+134134+ return err ? err : count;135135+136136+}137137+138138+static ssize_t bcm43xx_attr_interfmode_show(struct device *dev,139139+ struct device_attribute *attr,140140+ char *buf)141141+{142142+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode);143143+ unsigned long flags;144144+ int err;145145+ ssize_t count = 0;146146+147147+ if (!capable(CAP_NET_ADMIN))148148+ return -EPERM;149149+150150+ bcm43xx_lock(bcm, flags);151151+ assert(bcm->initialized);152152+153153+ switch (bcm43xx_current_radio(bcm)->interfmode) {154154+ case BCM43xx_RADIO_INTERFMODE_NONE:155155+ count = snprintf(buf, PAGE_SIZE, "0 (No Interference Mitigation)\n");156156+ break;157157+ case BCM43xx_RADIO_INTERFMODE_NONWLAN:158158+ count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference Mitigation)\n");159159+ break;160160+ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:161161+ count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference Mitigation)\n");162162+ break;163163+ default:164164+ assert(0);165165+ }166166+ err = 0;167167+168168+ bcm43xx_unlock(bcm, flags);169169+170170+ return err ? err : count;171171+172172+}173173+174174+static ssize_t bcm43xx_attr_interfmode_store(struct device *dev,175175+ struct device_attribute *attr,176176+ const char *buf, size_t count)177177+{178178+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode);179179+ unsigned long flags;180180+ int err;181181+ int mode;182182+183183+ if (!capable(CAP_NET_ADMIN))184184+ return -EPERM;185185+186186+ mode = get_integer(buf, count);187187+ switch (mode) {188188+ case 0:189189+ mode = BCM43xx_RADIO_INTERFMODE_NONE;190190+ break;191191+ case 1:192192+ mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;193193+ break;194194+ case 2:195195+ mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;196196+ break;197197+ case 3:198198+ mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;199199+ break;200200+ default:201201+ return -EINVAL;202202+ }203203+204204+ bcm43xx_lock_mmio(bcm, flags);205205+ assert(bcm->initialized);206206+207207+ err = bcm43xx_radio_set_interference_mitigation(bcm, mode);208208+ if (err) {209209+ printk(KERN_ERR PFX "Interference Mitigation not "210210+ "supported by device\n");211211+ }212212+213213+ bcm43xx_unlock_mmio(bcm, flags);214214+215215+ return err ? err : count;216216+}217217+218218+static ssize_t bcm43xx_attr_preamble_show(struct device *dev,219219+ struct device_attribute *attr,220220+ char *buf)221221+{222222+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble);223223+ unsigned long flags;224224+ int err;225225+ ssize_t count;226226+227227+ if (!capable(CAP_NET_ADMIN))228228+ return -EPERM;229229+230230+ bcm43xx_lock(bcm, flags);231231+ assert(bcm->initialized);232232+233233+ if (bcm->short_preamble)234234+ count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble enabled)\n");235235+ else236236+ count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble disabled)\n");237237+238238+ err = 0;239239+ bcm43xx_unlock(bcm, flags);240240+241241+ return err ? err : count;242242+}243243+244244+static ssize_t bcm43xx_attr_preamble_store(struct device *dev,245245+ struct device_attribute *attr,246246+ const char *buf, size_t count)247247+{248248+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble);249249+ unsigned long flags;250250+ int err;251251+ int value;252252+253253+ if (!capable(CAP_NET_ADMIN))254254+ return -EPERM;255255+256256+ value = get_boolean(buf, count);257257+ if (value < 0)258258+ return value;259259+ bcm43xx_lock(bcm, flags);260260+ assert(bcm->initialized);261261+262262+ bcm->short_preamble = !!value;263263+264264+ err = 0;265265+ bcm43xx_unlock(bcm, flags);266266+267267+ return err ? err : count;268268+}269269+270270+int bcm43xx_sysfs_register(struct bcm43xx_private *bcm)271271+{272272+ struct device *dev = &bcm->pci_dev->dev;273273+ struct bcm43xx_sysfs *sysfs = &bcm->sysfs;274274+ int err;275275+276276+ assert(bcm->initialized);277277+278278+ sysfs->attr_sprom.attr.name = "sprom";279279+ sysfs->attr_sprom.attr.owner = THIS_MODULE;280280+ sysfs->attr_sprom.attr.mode = 0600;281281+ sysfs->attr_sprom.show = bcm43xx_attr_sprom_show;282282+ sysfs->attr_sprom.store = bcm43xx_attr_sprom_store;283283+ err = device_create_file(dev, &sysfs->attr_sprom);284284+ if (err)285285+ goto out;286286+287287+ sysfs->attr_interfmode.attr.name = "interference";288288+ sysfs->attr_interfmode.attr.owner = THIS_MODULE;289289+ sysfs->attr_interfmode.attr.mode = 0600;290290+ sysfs->attr_interfmode.show = bcm43xx_attr_interfmode_show;291291+ sysfs->attr_interfmode.store = bcm43xx_attr_interfmode_store;292292+ err = device_create_file(dev, &sysfs->attr_interfmode);293293+ if (err)294294+ goto err_remove_sprom;295295+296296+ sysfs->attr_preamble.attr.name = "shortpreamble";297297+ sysfs->attr_preamble.attr.owner = THIS_MODULE;298298+ sysfs->attr_preamble.attr.mode = 0600;299299+ sysfs->attr_preamble.show = bcm43xx_attr_preamble_show;300300+ sysfs->attr_preamble.store = bcm43xx_attr_preamble_store;301301+ err = device_create_file(dev, &sysfs->attr_preamble);302302+ if (err)303303+ goto err_remove_interfmode;304304+305305+out:306306+ return err;307307+err_remove_interfmode:308308+ device_remove_file(dev, &sysfs->attr_interfmode);309309+err_remove_sprom:310310+ device_remove_file(dev, &sysfs->attr_sprom);311311+ goto out;312312+}313313+314314+void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm)315315+{316316+ struct device *dev = &bcm->pci_dev->dev;317317+ struct bcm43xx_sysfs *sysfs = &bcm->sysfs;318318+319319+ device_remove_file(dev, &sysfs->attr_preamble);320320+ device_remove_file(dev, &sysfs->attr_interfmode);321321+ device_remove_file(dev, &sysfs->attr_sprom);322322+}
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,66+ Stefano Brivio <st3@riseup.net>77+ Michael Buesch <mbuesch@freenet.de>88+ Danny van Dyk <kugelfang@gentoo.org>99+ Andreas Jaggi <andreas.jaggi@waterwave.ch>1010+1111+ Some parts of the code in this file are derived from the ipw22001212+ driver Copyright(c) 2003 - 2004 Intel Corporation.1313+1414+ This program is free software; you can redistribute it and/or modify1515+ it under the terms of the GNU General Public License as published by1616+ the Free Software Foundation; either version 2 of the License, or1717+ (at your option) any later version.1818+1919+ This program is distributed in the hope that it will be useful,2020+ but WITHOUT ANY WARRANTY; without even the implied warranty of2121+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the2222+ GNU General Public License for more details.2323+2424+ You should have received a copy of the GNU General Public License2525+ along with this program; see the file COPYING. If not, write to2626+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2727+ Boston, MA 02110-1301, USA.2828+2929+*/3030+3131+#include <linux/wireless.h>3232+#include <net/iw_handler.h>3333+#include <net/ieee80211softmac.h>3434+#include <net/ieee80211softmac_wx.h>3535+#include <linux/capability.h>3636+#include <linux/sched.h> /* for capable() */3737+#include <linux/delay.h>3838+3939+#include "bcm43xx.h"4040+#include "bcm43xx_wx.h"4141+#include "bcm43xx_main.h"4242+#include "bcm43xx_radio.h"4343+#include "bcm43xx_phy.h"4444+4545+4646+/* The WIRELESS_EXT version, which is implemented by this driver. */4747+#define BCM43xx_WX_VERSION 184848+4949+#define MAX_WX_STRING 805050+5151+5252+static int bcm43xx_wx_get_name(struct net_device *net_dev,5353+ struct iw_request_info *info,5454+ union iwreq_data *data,5555+ char *extra)5656+{5757+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);5858+ unsigned long flags;5959+ int i;6060+ struct bcm43xx_phyinfo *phy;6161+ char suffix[7] = { 0 };6262+ int have_a = 0, have_b = 0, have_g = 0;6363+6464+ bcm43xx_lock(bcm, flags);6565+ for (i = 0; i < bcm->nr_80211_available; i++) {6666+ phy = &(bcm->core_80211_ext[i].phy);6767+ switch (phy->type) {6868+ case BCM43xx_PHYTYPE_A:6969+ have_a = 1;7070+ break;7171+ case BCM43xx_PHYTYPE_G:7272+ have_g = 1;7373+ case BCM43xx_PHYTYPE_B:7474+ have_b = 1;7575+ break;7676+ default:7777+ assert(0);7878+ }7979+ }8080+ bcm43xx_unlock(bcm, flags);8181+8282+ i = 0;8383+ if (have_a) {8484+ suffix[i++] = 'a';8585+ suffix[i++] = '/';8686+ }8787+ if (have_b) {8888+ suffix[i++] = 'b';8989+ suffix[i++] = '/';9090+ }9191+ if (have_g) {9292+ suffix[i++] = 'g';9393+ suffix[i++] = '/';9494+ }9595+ if (i != 0) 9696+ suffix[i - 1] = '\0';9797+9898+ snprintf(data->name, IFNAMSIZ, "IEEE 802.11%s", suffix);9999+100100+ return 0;101101+}102102+103103+static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev,104104+ struct iw_request_info *info,105105+ union iwreq_data *data,106106+ char *extra)107107+{108108+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);109109+ unsigned long flags;110110+ u8 channel;111111+ int freq;112112+ int err = -EINVAL;113113+114114+ bcm43xx_lock_mmio(bcm, flags);115115+ if ((data->freq.m >= 0) && (data->freq.m <= 1000)) {116116+ channel = data->freq.m;117117+ freq = bcm43xx_channel_to_freq(bcm, channel);118118+ } else {119119+ channel = bcm43xx_freq_to_channel(bcm, data->freq.m);120120+ freq = data->freq.m;121121+ }122122+ if (!bcm43xx_is_valid_channel(bcm, channel))123123+ goto out_unlock;124124+ if (bcm->initialized) {125125+ //ieee80211softmac_disassoc(softmac, $REASON);126126+ bcm43xx_mac_suspend(bcm);127127+ err = bcm43xx_radio_selectchannel(bcm, channel, 0);128128+ bcm43xx_mac_enable(bcm);129129+ } else {130130+ bcm43xx_current_radio(bcm)->initial_channel = channel;131131+ err = 0;132132+ }133133+out_unlock:134134+ bcm43xx_unlock_mmio(bcm, flags);135135+136136+ return err;137137+}138138+139139+static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev,140140+ struct iw_request_info *info,141141+ union iwreq_data *data,142142+ char *extra)143143+{144144+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);145145+ struct bcm43xx_radioinfo *radio;146146+ unsigned long flags;147147+ int err = -ENODEV;148148+ u16 channel;149149+150150+ bcm43xx_lock(bcm, flags);151151+ radio = bcm43xx_current_radio(bcm);152152+ channel = radio->channel;153153+ if (channel == 0xFF) {154154+ assert(!bcm->initialized);155155+ channel = radio->initial_channel;156156+ if (channel == 0xFF)157157+ goto out_unlock;158158+ }159159+ assert(channel > 0 && channel <= 1000);160160+ data->freq.e = 1;161161+ data->freq.m = bcm43xx_channel_to_freq(bcm, channel) * 100000;162162+ data->freq.flags = 1;163163+164164+ err = 0;165165+out_unlock:166166+ bcm43xx_unlock(bcm, flags);167167+168168+ return err;169169+}170170+171171+static int bcm43xx_wx_set_mode(struct net_device *net_dev,172172+ struct iw_request_info *info,173173+ union iwreq_data *data,174174+ char *extra)175175+{176176+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);177177+ unsigned long flags;178178+ int mode;179179+180180+ mode = data->mode;181181+ if (mode == IW_MODE_AUTO)182182+ mode = BCM43xx_INITIAL_IWMODE;183183+184184+ bcm43xx_lock_mmio(bcm, flags);185185+ if (bcm->ieee->iw_mode != mode)186186+ bcm43xx_set_iwmode(bcm, mode);187187+ bcm43xx_unlock_mmio(bcm, flags);188188+189189+ return 0;190190+}191191+192192+static int bcm43xx_wx_get_mode(struct net_device *net_dev,193193+ struct iw_request_info *info,194194+ union iwreq_data *data,195195+ char *extra)196196+{197197+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);198198+ unsigned long flags;199199+200200+ bcm43xx_lock(bcm, flags);201201+ data->mode = bcm->ieee->iw_mode;202202+ bcm43xx_unlock(bcm, flags);203203+204204+ return 0;205205+}206206+207207+static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev,208208+ struct iw_request_info *info,209209+ union iwreq_data *data,210210+ char *extra)211211+{212212+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);213213+ struct iw_range *range = (struct iw_range *)extra;214214+ const struct ieee80211_geo *geo;215215+ unsigned long flags;216216+ int i, j;217217+ struct bcm43xx_phyinfo *phy;218218+219219+ data->data.length = sizeof(*range);220220+ memset(range, 0, sizeof(*range));221221+222222+ //TODO: What about 802.11b?223223+ /* 54Mb/s == ~27Mb/s payload throughput (802.11g) */224224+ range->throughput = 27 * 1000 * 1000;225225+226226+ range->max_qual.qual = 100;227227+ /* TODO: Real max RSSI */228228+ range->max_qual.level = 3;229229+ range->max_qual.noise = 100;230230+ range->max_qual.updated = 7;231231+232232+ range->avg_qual.qual = 70;233233+ range->avg_qual.level = 2;234234+ range->avg_qual.noise = 40;235235+ range->avg_qual.updated = 7;236236+237237+ range->min_rts = BCM43xx_MIN_RTS_THRESHOLD;238238+ range->max_rts = BCM43xx_MAX_RTS_THRESHOLD;239239+ range->min_frag = MIN_FRAG_THRESHOLD;240240+ range->max_frag = MAX_FRAG_THRESHOLD;241241+242242+ range->encoding_size[0] = 5;243243+ range->encoding_size[1] = 13;244244+ range->num_encoding_sizes = 2;245245+ range->max_encoding_tokens = WEP_KEYS;246246+247247+ range->we_version_compiled = WIRELESS_EXT;248248+ range->we_version_source = BCM43xx_WX_VERSION;249249+250250+ range->enc_capa = IW_ENC_CAPA_WPA |251251+ IW_ENC_CAPA_WPA2 |252252+ IW_ENC_CAPA_CIPHER_TKIP |253253+ IW_ENC_CAPA_CIPHER_CCMP;254254+255255+ bcm43xx_lock(bcm, flags);256256+ phy = bcm43xx_current_phy(bcm);257257+258258+ range->num_bitrates = 0;259259+ i = 0;260260+ if (phy->type == BCM43xx_PHYTYPE_A ||261261+ phy->type == BCM43xx_PHYTYPE_G) {262262+ range->num_bitrates = 8;263263+ range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB;264264+ range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB;265265+ range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB;266266+ range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB;267267+ range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB;268268+ range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB;269269+ range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB;270270+ range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB;271271+ }272272+ if (phy->type == BCM43xx_PHYTYPE_B ||273273+ phy->type == BCM43xx_PHYTYPE_G) {274274+ range->num_bitrates += 4;275275+ range->bitrate[i++] = IEEE80211_CCK_RATE_1MB;276276+ range->bitrate[i++] = IEEE80211_CCK_RATE_2MB;277277+ range->bitrate[i++] = IEEE80211_CCK_RATE_5MB;278278+ range->bitrate[i++] = IEEE80211_CCK_RATE_11MB;279279+ }280280+281281+ geo = ieee80211_get_geo(bcm->ieee);282282+ range->num_channels = geo->a_channels + geo->bg_channels;283283+ j = 0;284284+ for (i = 0; i < geo->a_channels; i++) {285285+ if (j == IW_MAX_FREQUENCIES)286286+ break;287287+ range->freq[j].i = j + 1;288288+ range->freq[j].m = geo->a[i].freq;//FIXME?289289+ range->freq[j].e = 1;290290+ j++;291291+ }292292+ for (i = 0; i < geo->bg_channels; i++) {293293+ if (j == IW_MAX_FREQUENCIES)294294+ break;295295+ range->freq[j].i = j + 1;296296+ range->freq[j].m = geo->bg[i].freq;//FIXME?297297+ range->freq[j].e = 1;298298+ j++;299299+ }300300+ range->num_frequency = j;301301+302302+ bcm43xx_unlock(bcm, flags);303303+304304+ return 0;305305+}306306+307307+static int bcm43xx_wx_set_nick(struct net_device *net_dev,308308+ struct iw_request_info *info,309309+ union iwreq_data *data,310310+ char *extra)311311+{312312+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);313313+ unsigned long flags;314314+ size_t len;315315+316316+ bcm43xx_lock(bcm, flags);317317+ len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE);318318+ memcpy(bcm->nick, extra, len);319319+ bcm->nick[len] = '\0';320320+ bcm43xx_unlock(bcm, flags);321321+322322+ return 0;323323+}324324+325325+static int bcm43xx_wx_get_nick(struct net_device *net_dev,326326+ struct iw_request_info *info,327327+ union iwreq_data *data,328328+ char *extra)329329+{330330+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);331331+ unsigned long flags;332332+ size_t len;333333+334334+ bcm43xx_lock(bcm, flags);335335+ len = strlen(bcm->nick) + 1;336336+ memcpy(extra, bcm->nick, len);337337+ data->data.length = (__u16)len;338338+ data->data.flags = 1;339339+ bcm43xx_unlock(bcm, flags);340340+341341+ return 0;342342+}343343+344344+static int bcm43xx_wx_set_rts(struct net_device *net_dev,345345+ struct iw_request_info *info,346346+ union iwreq_data *data,347347+ char *extra)348348+{349349+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);350350+ unsigned long flags;351351+ int err = -EINVAL;352352+353353+ bcm43xx_lock(bcm, flags);354354+ if (data->rts.disabled) {355355+ bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD;356356+ err = 0;357357+ } else {358358+ if (data->rts.value >= BCM43xx_MIN_RTS_THRESHOLD &&359359+ data->rts.value <= BCM43xx_MAX_RTS_THRESHOLD) {360360+ bcm->rts_threshold = data->rts.value;361361+ err = 0;362362+ }363363+ }364364+ bcm43xx_unlock(bcm, flags);365365+366366+ return err;367367+}368368+369369+static int bcm43xx_wx_get_rts(struct net_device *net_dev,370370+ struct iw_request_info *info,371371+ union iwreq_data *data,372372+ char *extra)373373+{374374+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);375375+ unsigned long flags;376376+377377+ bcm43xx_lock(bcm, flags);378378+ data->rts.value = bcm->rts_threshold;379379+ data->rts.fixed = 0;380380+ data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD);381381+ bcm43xx_unlock(bcm, flags);382382+383383+ return 0;384384+}385385+386386+static int bcm43xx_wx_set_frag(struct net_device *net_dev,387387+ struct iw_request_info *info,388388+ union iwreq_data *data,389389+ char *extra)390390+{391391+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);392392+ unsigned long flags;393393+ int err = -EINVAL;394394+395395+ bcm43xx_lock(bcm, flags);396396+ if (data->frag.disabled) {397397+ bcm->ieee->fts = MAX_FRAG_THRESHOLD;398398+ err = 0;399399+ } else {400400+ if (data->frag.value >= MIN_FRAG_THRESHOLD &&401401+ data->frag.value <= MAX_FRAG_THRESHOLD) {402402+ bcm->ieee->fts = data->frag.value & ~0x1;403403+ err = 0;404404+ }405405+ }406406+ bcm43xx_unlock(bcm, flags);407407+408408+ return err;409409+}410410+411411+static int bcm43xx_wx_get_frag(struct net_device *net_dev,412412+ struct iw_request_info *info,413413+ union iwreq_data *data,414414+ char *extra)415415+{416416+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);417417+ unsigned long flags;418418+419419+ bcm43xx_lock(bcm, flags);420420+ data->frag.value = bcm->ieee->fts;421421+ data->frag.fixed = 0;422422+ data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD);423423+ bcm43xx_unlock(bcm, flags);424424+425425+ return 0;426426+}427427+428428+static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev,429429+ struct iw_request_info *info,430430+ union iwreq_data *data,431431+ char *extra)432432+{433433+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);434434+ struct bcm43xx_radioinfo *radio;435435+ struct bcm43xx_phyinfo *phy;436436+ unsigned long flags;437437+ int err = -ENODEV;438438+ u16 maxpower;439439+440440+ if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) {441441+ printk(PFX KERN_ERR "TX power not in dBm.\n");442442+ return -EOPNOTSUPP;443443+ }444444+445445+ bcm43xx_lock_mmio(bcm, flags);446446+ if (!bcm->initialized)447447+ goto out_unlock;448448+ radio = bcm43xx_current_radio(bcm);449449+ phy = bcm43xx_current_phy(bcm);450450+ if (data->txpower.disabled != (!(radio->enabled))) {451451+ if (data->txpower.disabled)452452+ bcm43xx_radio_turn_off(bcm);453453+ else454454+ bcm43xx_radio_turn_on(bcm);455455+ }456456+ if (data->txpower.value > 0) {457457+ /* desired and maxpower dBm values are in Q5.2 */458458+ if (phy->type == BCM43xx_PHYTYPE_A)459459+ maxpower = bcm->sprom.maxpower_aphy;460460+ else461461+ maxpower = bcm->sprom.maxpower_bgphy;462462+ radio->txpower_desired = limit_value(data->txpower.value << 2,463463+ 0, maxpower);464464+ bcm43xx_phy_xmitpower(bcm);465465+ }466466+ err = 0;467467+468468+out_unlock:469469+ bcm43xx_unlock_mmio(bcm, flags);470470+471471+ return err;472472+}473473+474474+static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev,475475+ struct iw_request_info *info,476476+ union iwreq_data *data,477477+ char *extra)478478+{479479+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);480480+ struct bcm43xx_radioinfo *radio;481481+ unsigned long flags;482482+ int err = -ENODEV;483483+484484+ bcm43xx_lock(bcm, flags);485485+ if (!bcm->initialized)486486+ goto out_unlock;487487+ radio = bcm43xx_current_radio(bcm);488488+ /* desired dBm value is in Q5.2 */489489+ data->txpower.value = radio->txpower_desired >> 2;490490+ data->txpower.fixed = 1;491491+ data->txpower.flags = IW_TXPOW_DBM;492492+ data->txpower.disabled = !(radio->enabled);493493+494494+ err = 0;495495+out_unlock:496496+ bcm43xx_unlock(bcm, flags);497497+498498+ return err;499499+}500500+501501+static int bcm43xx_wx_set_encoding(struct net_device *net_dev,502502+ struct iw_request_info *info,503503+ union iwreq_data *data,504504+ char *extra)505505+{506506+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);507507+ int err;508508+509509+ err = ieee80211_wx_set_encode(bcm->ieee, info, data, extra);510510+511511+ return err;512512+}513513+514514+static int bcm43xx_wx_set_encodingext(struct net_device *net_dev,515515+ struct iw_request_info *info,516516+ union iwreq_data *data,517517+ char *extra)518518+{519519+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);520520+ int err;521521+522522+ err = ieee80211_wx_set_encodeext(bcm->ieee, info, data, extra);523523+524524+ return err;525525+}526526+527527+static int bcm43xx_wx_get_encoding(struct net_device *net_dev,528528+ struct iw_request_info *info,529529+ union iwreq_data *data,530530+ char *extra)531531+{532532+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);533533+ int err;534534+535535+ err = ieee80211_wx_get_encode(bcm->ieee, info, data, extra);536536+537537+ return err;538538+}539539+540540+static int bcm43xx_wx_get_encodingext(struct net_device *net_dev,541541+ struct iw_request_info *info,542542+ union iwreq_data *data,543543+ char *extra)544544+{545545+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);546546+ int err;547547+548548+ err = ieee80211_wx_get_encodeext(bcm->ieee, info, data, extra);549549+550550+ return err;551551+}552552+553553+static int bcm43xx_wx_set_interfmode(struct net_device *net_dev,554554+ struct iw_request_info *info,555555+ union iwreq_data *data,556556+ char *extra)557557+{558558+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);559559+ unsigned long flags;560560+ int mode, err = 0;561561+562562+ mode = *((int *)extra);563563+ switch (mode) {564564+ case 0:565565+ mode = BCM43xx_RADIO_INTERFMODE_NONE;566566+ break;567567+ case 1:568568+ mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;569569+ break;570570+ case 2:571571+ mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;572572+ break;573573+ case 3:574574+ mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;575575+ break;576576+ default:577577+ printk(KERN_ERR PFX "set_interfmode allowed parameters are: "578578+ "0 => None, 1 => Non-WLAN, 2 => WLAN, "579579+ "3 => Auto-WLAN\n");580580+ return -EINVAL;581581+ }582582+583583+ bcm43xx_lock_mmio(bcm, flags);584584+ if (bcm->initialized) {585585+ err = bcm43xx_radio_set_interference_mitigation(bcm, mode);586586+ if (err) {587587+ printk(KERN_ERR PFX "Interference Mitigation not "588588+ "supported by device\n");589589+ }590590+ } else {591591+ if (mode == BCM43xx_RADIO_INTERFMODE_AUTOWLAN) {592592+ printk(KERN_ERR PFX "Interference Mitigation mode Auto-WLAN "593593+ "not supported while the interface is down.\n");594594+ err = -ENODEV;595595+ } else596596+ bcm43xx_current_radio(bcm)->interfmode = mode;597597+ }598598+ bcm43xx_unlock_mmio(bcm, flags);599599+600600+ return err;601601+}602602+603603+static int bcm43xx_wx_get_interfmode(struct net_device *net_dev,604604+ struct iw_request_info *info,605605+ union iwreq_data *data,606606+ char *extra)607607+{608608+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);609609+ unsigned long flags;610610+ int mode;611611+612612+ bcm43xx_lock(bcm, flags);613613+ mode = bcm43xx_current_radio(bcm)->interfmode;614614+ bcm43xx_unlock(bcm, flags);615615+616616+ switch (mode) {617617+ case BCM43xx_RADIO_INTERFMODE_NONE:618618+ strncpy(extra, "0 (No Interference Mitigation)", MAX_WX_STRING);619619+ break;620620+ case BCM43xx_RADIO_INTERFMODE_NONWLAN:621621+ strncpy(extra, "1 (Non-WLAN Interference Mitigation)", MAX_WX_STRING);622622+ break;623623+ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:624624+ strncpy(extra, "2 (WLAN Interference Mitigation)", MAX_WX_STRING);625625+ break;626626+ default:627627+ assert(0);628628+ }629629+ data->data.length = strlen(extra) + 1;630630+631631+ return 0;632632+}633633+634634+static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev,635635+ struct iw_request_info *info,636636+ union iwreq_data *data,637637+ char *extra)638638+{639639+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);640640+ unsigned long flags;641641+ int on;642642+643643+ on = *((int *)extra);644644+ bcm43xx_lock(bcm, flags);645645+ bcm->short_preamble = !!on;646646+ bcm43xx_unlock(bcm, flags);647647+648648+ return 0;649649+}650650+651651+static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev,652652+ struct iw_request_info *info,653653+ union iwreq_data *data,654654+ char *extra)655655+{656656+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);657657+ unsigned long flags;658658+ int on;659659+660660+ bcm43xx_lock(bcm, flags);661661+ on = bcm->short_preamble;662662+ bcm43xx_unlock(bcm, flags);663663+664664+ if (on)665665+ strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING);666666+ else667667+ strncpy(extra, "0 (Short Preamble disabled)", MAX_WX_STRING);668668+ data->data.length = strlen(extra) + 1;669669+670670+ return 0;671671+}672672+673673+static int bcm43xx_wx_set_swencryption(struct net_device *net_dev,674674+ struct iw_request_info *info,675675+ union iwreq_data *data,676676+ char *extra)677677+{678678+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);679679+ unsigned long flags;680680+ int on;681681+682682+ on = *((int *)extra);683683+684684+ bcm43xx_lock(bcm, flags);685685+ bcm->ieee->host_encrypt = !!on;686686+ bcm->ieee->host_decrypt = !!on;687687+ bcm->ieee->host_build_iv = !on;688688+ bcm43xx_unlock(bcm, flags);689689+690690+ return 0;691691+}692692+693693+static int bcm43xx_wx_get_swencryption(struct net_device *net_dev,694694+ struct iw_request_info *info,695695+ union iwreq_data *data,696696+ char *extra)697697+{698698+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);699699+ unsigned long flags;700700+ int on;701701+702702+ bcm43xx_lock(bcm, flags);703703+ on = bcm->ieee->host_encrypt;704704+ bcm43xx_unlock(bcm, flags);705705+706706+ if (on)707707+ strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING);708708+ else709709+ strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING);710710+ data->data.length = strlen(extra + 1);711711+712712+ return 0;713713+}714714+715715+/* Enough buffer to hold a hexdump of the sprom data. */716716+#define SPROM_BUFFERSIZE 512717717+718718+static int sprom2hex(const u16 *sprom, char *dump)719719+{720720+ int i, pos = 0;721721+722722+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {723723+ pos += snprintf(dump + pos, SPROM_BUFFERSIZE - pos - 1,724724+ "%04X", swab16(sprom[i]) & 0xFFFF);725725+ }726726+727727+ return pos + 1;728728+}729729+730730+static int hex2sprom(u16 *sprom, const char *dump, unsigned int len)731731+{732732+ char tmp[5] = { 0 };733733+ int cnt = 0;734734+ unsigned long parsed;735735+736736+ if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2)737737+ return -EINVAL;738738+ while (cnt < BCM43xx_SPROM_SIZE) {739739+ memcpy(tmp, dump, 4);740740+ dump += 4;741741+ parsed = simple_strtoul(tmp, NULL, 16);742742+ sprom[cnt++] = swab16((u16)parsed);743743+ }744744+745745+ return 0;746746+}747747+748748+static int bcm43xx_wx_sprom_read(struct net_device *net_dev,749749+ struct iw_request_info *info,750750+ union iwreq_data *data,751751+ char *extra)752752+{753753+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);754754+ int err = -EPERM;755755+ u16 *sprom;756756+ unsigned long flags;757757+758758+ if (!capable(CAP_SYS_RAWIO))759759+ goto out;760760+761761+ err = -ENOMEM;762762+ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),763763+ GFP_KERNEL);764764+ if (!sprom)765765+ goto out;766766+767767+ bcm43xx_lock_mmio(bcm, flags);768768+ err = -ENODEV;769769+ if (bcm->initialized)770770+ err = bcm43xx_sprom_read(bcm, sprom);771771+ bcm43xx_unlock_mmio(bcm, flags);772772+ if (!err)773773+ data->data.length = sprom2hex(sprom, extra);774774+ kfree(sprom);775775+out:776776+ return err;777777+}778778+779779+static int bcm43xx_wx_sprom_write(struct net_device *net_dev,780780+ struct iw_request_info *info,781781+ union iwreq_data *data,782782+ char *extra)783783+{784784+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);785785+ int err = -EPERM;786786+ u16 *sprom;787787+ unsigned long flags;788788+ char *input;789789+ unsigned int len;790790+791791+ if (!capable(CAP_SYS_RAWIO))792792+ goto out;793793+794794+ err = -ENOMEM;795795+ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),796796+ GFP_KERNEL);797797+ if (!sprom)798798+ goto out;799799+800800+ len = data->data.length;801801+ extra[len - 1] = '\0';802802+ input = strchr(extra, ':');803803+ if (input) {804804+ input++;805805+ len -= input - extra;806806+ } else807807+ input = extra;808808+ err = hex2sprom(sprom, input, len);809809+ if (err)810810+ goto out_kfree;811811+812812+ bcm43xx_lock_mmio(bcm, flags);813813+ err = -ENODEV;814814+ if (bcm->initialized)815815+ err = bcm43xx_sprom_write(bcm, sprom);816816+ bcm43xx_unlock_mmio(bcm, flags);817817+out_kfree:818818+ kfree(sprom);819819+out:820820+ return err;821821+}822822+823823+/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */824824+825825+static struct iw_statistics *bcm43xx_get_wireless_stats(struct net_device *net_dev)826826+{827827+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);828828+ struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);829829+ struct iw_statistics *wstats;830830+831831+ wstats = &bcm->stats.wstats;832832+ if (!mac->associated) {833833+ wstats->miss.beacon = 0;834834+// bcm->ieee->ieee_stats.tx_retry_limit_exceeded = 0; // FIXME: should this be cleared here?835835+ wstats->discard.retries = 0;836836+// bcm->ieee->ieee_stats.tx_discards_wrong_sa = 0; // FIXME: same question837837+ wstats->discard.nwid = 0;838838+// bcm->ieee->ieee_stats.rx_discards_undecryptable = 0; // FIXME: ditto839839+ wstats->discard.code = 0;840840+// bcm->ieee->ieee_stats.rx_fragments = 0; // FIXME: same here841841+ wstats->discard.fragment = 0;842842+ wstats->discard.misc = 0;843843+ wstats->qual.qual = 0;844844+ wstats->qual.level = 0;845845+ wstats->qual.noise = 0;846846+ wstats->qual.updated = 7;847847+ wstats->qual.updated |= IW_QUAL_NOISE_INVALID |848848+ IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;849849+ return wstats;850850+ }851851+ /* fill in the real statistics when iface associated */852852+ wstats->qual.qual = 100; // TODO: get the real signal quality853853+ wstats->qual.level = 3 - bcm->stats.link_quality;854854+ wstats->qual.noise = bcm->stats.noise;855855+ wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |856856+ IW_QUAL_NOISE_UPDATED;857857+ wstats->discard.code = bcm->ieee->ieee_stats.rx_discards_undecryptable;858858+ wstats->discard.retries = bcm->ieee->ieee_stats.tx_retry_limit_exceeded;859859+ wstats->discard.nwid = bcm->ieee->ieee_stats.tx_discards_wrong_sa;860860+ wstats->discard.fragment = bcm->ieee->ieee_stats.rx_fragments;861861+ wstats->discard.misc = 0; // FIXME862862+ wstats->miss.beacon = 0; // FIXME863863+ return wstats;864864+}865865+866866+867867+#ifdef WX868868+# undef WX869869+#endif870870+#define WX(ioctl) [(ioctl) - SIOCSIWCOMMIT]871871+static const iw_handler bcm43xx_wx_handlers[] = {872872+ /* Wireless Identification */873873+ WX(SIOCGIWNAME) = bcm43xx_wx_get_name,874874+ /* Basic operations */875875+ WX(SIOCSIWFREQ) = bcm43xx_wx_set_channelfreq,876876+ WX(SIOCGIWFREQ) = bcm43xx_wx_get_channelfreq,877877+ WX(SIOCSIWMODE) = bcm43xx_wx_set_mode,878878+ WX(SIOCGIWMODE) = bcm43xx_wx_get_mode,879879+ /* Informative stuff */880880+ WX(SIOCGIWRANGE) = bcm43xx_wx_get_rangeparams,881881+ /* Access Point manipulation */882882+ WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap,883883+ WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap,884884+ WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan,885885+ WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results,886886+ /* 802.11 specific support */887887+ WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid,888888+ WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid,889889+ WX(SIOCSIWNICKN) = bcm43xx_wx_set_nick,890890+ WX(SIOCGIWNICKN) = bcm43xx_wx_get_nick,891891+ /* Other parameters */892892+ WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate,893893+ WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate,894894+ WX(SIOCSIWRTS) = bcm43xx_wx_set_rts,895895+ WX(SIOCGIWRTS) = bcm43xx_wx_get_rts,896896+ WX(SIOCSIWFRAG) = bcm43xx_wx_set_frag,897897+ WX(SIOCGIWFRAG) = bcm43xx_wx_get_frag,898898+ WX(SIOCSIWTXPOW) = bcm43xx_wx_set_xmitpower,899899+ WX(SIOCGIWTXPOW) = bcm43xx_wx_get_xmitpower,900900+//TODO WX(SIOCSIWRETRY) = bcm43xx_wx_set_retry,901901+//TODO WX(SIOCGIWRETRY) = bcm43xx_wx_get_retry,902902+ /* Encoding */903903+ WX(SIOCSIWENCODE) = bcm43xx_wx_set_encoding,904904+ WX(SIOCGIWENCODE) = bcm43xx_wx_get_encoding,905905+ WX(SIOCSIWENCODEEXT) = bcm43xx_wx_set_encodingext,906906+ WX(SIOCGIWENCODEEXT) = bcm43xx_wx_get_encodingext,907907+ /* Power saving */908908+//TODO WX(SIOCSIWPOWER) = bcm43xx_wx_set_power,909909+//TODO WX(SIOCGIWPOWER) = bcm43xx_wx_get_power,910910+ WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie,911911+ WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie,912912+ WX(SIOCSIWAUTH) = ieee80211_wx_set_auth,913913+ WX(SIOCGIWAUTH) = ieee80211_wx_get_auth,914914+};915915+#undef WX916916+917917+static const iw_handler bcm43xx_priv_wx_handlers[] = {918918+ /* Set Interference Mitigation Mode. */919919+ bcm43xx_wx_set_interfmode,920920+ /* Get Interference Mitigation Mode. */921921+ bcm43xx_wx_get_interfmode,922922+ /* Enable/Disable Short Preamble mode. */923923+ bcm43xx_wx_set_shortpreamble,924924+ /* Get Short Preamble mode. */925925+ bcm43xx_wx_get_shortpreamble,926926+ /* Enable/Disable Software Encryption mode */927927+ bcm43xx_wx_set_swencryption,928928+ /* Get Software Encryption mode */929929+ bcm43xx_wx_get_swencryption,930930+ /* Write SRPROM data. */931931+ bcm43xx_wx_sprom_write,932932+ /* Read SPROM data. */933933+ bcm43xx_wx_sprom_read,934934+};935935+936936+#define PRIV_WX_SET_INTERFMODE (SIOCIWFIRSTPRIV + 0)937937+#define PRIV_WX_GET_INTERFMODE (SIOCIWFIRSTPRIV + 1)938938+#define PRIV_WX_SET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 2)939939+#define PRIV_WX_GET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 3)940940+#define PRIV_WX_SET_SWENCRYPTION (SIOCIWFIRSTPRIV + 4)941941+#define PRIV_WX_GET_SWENCRYPTION (SIOCIWFIRSTPRIV + 5)942942+#define PRIV_WX_SPROM_WRITE (SIOCIWFIRSTPRIV + 6)943943+#define PRIV_WX_SPROM_READ (SIOCIWFIRSTPRIV + 7)944944+945945+#define PRIV_WX_DUMMY(ioctl) \946946+ { \947947+ .cmd = (ioctl), \948948+ .name = "__unused" \949949+ }950950+951951+static const struct iw_priv_args bcm43xx_priv_wx_args[] = {952952+ {953953+ .cmd = PRIV_WX_SET_INTERFMODE,954954+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,955955+ .name = "set_interfmode",956956+ },957957+ {958958+ .cmd = PRIV_WX_GET_INTERFMODE,959959+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,960960+ .name = "get_interfmode",961961+ },962962+ {963963+ .cmd = PRIV_WX_SET_SHORTPREAMBLE,964964+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,965965+ .name = "set_shortpreambl",966966+ },967967+ {968968+ .cmd = PRIV_WX_GET_SHORTPREAMBLE,969969+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,970970+ .name = "get_shortpreambl",971971+ },972972+ {973973+ .cmd = PRIV_WX_SET_SWENCRYPTION,974974+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,975975+ .name = "set_swencryption",976976+ },977977+ {978978+ .cmd = PRIV_WX_GET_SWENCRYPTION,979979+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,980980+ .name = "get_swencryption",981981+ },982982+ {983983+ .cmd = PRIV_WX_SPROM_WRITE,984984+ .set_args = IW_PRIV_TYPE_CHAR | SPROM_BUFFERSIZE,985985+ .name = "write_sprom",986986+ },987987+ {988988+ .cmd = PRIV_WX_SPROM_READ,989989+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | SPROM_BUFFERSIZE,990990+ .name = "read_sprom",991991+ },992992+};993993+994994+const struct iw_handler_def bcm43xx_wx_handlers_def = {995995+ .standard = bcm43xx_wx_handlers,996996+ .num_standard = ARRAY_SIZE(bcm43xx_wx_handlers),997997+ .num_private = ARRAY_SIZE(bcm43xx_priv_wx_handlers),998998+ .num_private_args = ARRAY_SIZE(bcm43xx_priv_wx_args),999999+ .private = bcm43xx_priv_wx_handlers,10001000+ .private_args = bcm43xx_priv_wx_args,10011001+ .get_wireless_stats = bcm43xx_get_wireless_stats,10021002+};
+36
drivers/net/wireless/bcm43xx/bcm43xx_wx.h
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,66+ Stefano Brivio <st3@riseup.net>77+ Michael Buesch <mbuesch@freenet.de>88+ Danny van Dyk <kugelfang@gentoo.org>99+ Andreas Jaggi <andreas.jaggi@waterwave.ch>1010+1111+ Some parts of the code in this file are derived from the ipw22001212+ driver Copyright(c) 2003 - 2004 Intel Corporation.1313+1414+ This program is free software; you can redistribute it and/or modify1515+ it under the terms of the GNU General Public License as published by1616+ the Free Software Foundation; either version 2 of the License, or1717+ (at your option) any later version.1818+1919+ This program is distributed in the hope that it will be useful,2020+ but WITHOUT ANY WARRANTY; without even the implied warranty of2121+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the2222+ GNU General Public License for more details.2323+2424+ You should have received a copy of the GNU General Public License2525+ along with this program; see the file COPYING. If not, write to2626+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2727+ Boston, MA 02110-1301, USA.2828+2929+*/3030+3131+#ifndef BCM43xx_WX_H_3232+#define BCM43xx_WX_H_3333+3434+extern const struct iw_handler_def bcm43xx_wx_handlers_def;3535+3636+#endif /* BCM43xx_WX_H_ */
+582
drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
···11+/*22+33+ Broadcom BCM43xx wireless driver44+55+ Transmission (TX/RX) related functions.66+77+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,88+ Stefano Brivio <st3@riseup.net>99+ Michael Buesch <mbuesch@freenet.de>1010+ Danny van Dyk <kugelfang@gentoo.org>1111+ Andreas Jaggi <andreas.jaggi@waterwave.ch>1212+1313+ This program is free software; you can redistribute it and/or modify1414+ it under the terms of the GNU General Public License as published by1515+ the Free Software Foundation; either version 2 of the License, or1616+ (at your option) any later version.1717+1818+ This program is distributed in the hope that it will be useful,1919+ but WITHOUT ANY WARRANTY; without even the implied warranty of2020+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the2121+ GNU General Public License for more details.2222+2323+ You should have received a copy of the GNU General Public License2424+ along with this program; see the file COPYING. If not, write to2525+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,2626+ Boston, MA 02110-1301, USA.2727+2828+*/2929+3030+#include "bcm43xx_xmit.h"3131+3232+#include <linux/etherdevice.h>3333+3434+3535+/* Extract the bitrate out of a CCK PLCP header. */3636+static u8 bcm43xx_plcp_get_bitrate_cck(struct bcm43xx_plcp_hdr4 *plcp)3737+{3838+ switch (plcp->raw[0]) {3939+ case 0x0A:4040+ return IEEE80211_CCK_RATE_1MB;4141+ case 0x14:4242+ return IEEE80211_CCK_RATE_2MB;4343+ case 0x37:4444+ return IEEE80211_CCK_RATE_5MB;4545+ case 0x6E:4646+ return IEEE80211_CCK_RATE_11MB;4747+ }4848+ assert(0);4949+ return 0;5050+}5151+5252+/* Extract the bitrate out of an OFDM PLCP header. */5353+static u8 bcm43xx_plcp_get_bitrate_ofdm(struct bcm43xx_plcp_hdr4 *plcp)5454+{5555+ switch (plcp->raw[0] & 0xF) {5656+ case 0xB:5757+ return IEEE80211_OFDM_RATE_6MB;5858+ case 0xF:5959+ return IEEE80211_OFDM_RATE_9MB;6060+ case 0xA:6161+ return IEEE80211_OFDM_RATE_12MB;6262+ case 0xE:6363+ return IEEE80211_OFDM_RATE_18MB;6464+ case 0x9:6565+ return IEEE80211_OFDM_RATE_24MB;6666+ case 0xD:6767+ return IEEE80211_OFDM_RATE_36MB;6868+ case 0x8:6969+ return IEEE80211_OFDM_RATE_48MB;7070+ case 0xC:7171+ return IEEE80211_OFDM_RATE_54MB;7272+ }7373+ assert(0);7474+ return 0;7575+}7676+7777+u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate)7878+{7979+ switch (bitrate) {8080+ case IEEE80211_CCK_RATE_1MB:8181+ return 0x0A;8282+ case IEEE80211_CCK_RATE_2MB:8383+ return 0x14;8484+ case IEEE80211_CCK_RATE_5MB:8585+ return 0x37;8686+ case IEEE80211_CCK_RATE_11MB:8787+ return 0x6E;8888+ }8989+ assert(0);9090+ return 0;9191+}9292+9393+u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate)9494+{9595+ switch (bitrate) {9696+ case IEEE80211_OFDM_RATE_6MB:9797+ return 0xB;9898+ case IEEE80211_OFDM_RATE_9MB:9999+ return 0xF;100100+ case IEEE80211_OFDM_RATE_12MB:101101+ return 0xA;102102+ case IEEE80211_OFDM_RATE_18MB:103103+ return 0xE;104104+ case IEEE80211_OFDM_RATE_24MB:105105+ return 0x9;106106+ case IEEE80211_OFDM_RATE_36MB:107107+ return 0xD;108108+ case IEEE80211_OFDM_RATE_48MB:109109+ return 0x8;110110+ case IEEE80211_OFDM_RATE_54MB:111111+ return 0xC;112112+ }113113+ assert(0);114114+ return 0;115115+}116116+117117+static void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4 *plcp,118118+ const u16 octets, const u8 bitrate,119119+ const int ofdm_modulation)120120+{121121+ __le32 *data = &(plcp->data);122122+ __u8 *raw = plcp->raw;123123+124124+ if (ofdm_modulation) {125125+ *data = bcm43xx_plcp_get_ratecode_ofdm(bitrate);126126+ assert(!(octets & 0xF000));127127+ *data |= (octets << 5);128128+ *data = cpu_to_le32(*data);129129+ } else {130130+ u32 plen;131131+132132+ plen = octets * 16 / bitrate;133133+ if ((octets * 16 % bitrate) > 0) {134134+ plen++;135135+ if ((bitrate == IEEE80211_CCK_RATE_11MB)136136+ && ((octets * 8 % 11) < 4)) {137137+ raw[1] = 0x84;138138+ } else139139+ raw[1] = 0x04;140140+ } else141141+ raw[1] = 0x04;142142+ *data |= cpu_to_le32(plen << 16);143143+ raw[0] = bcm43xx_plcp_get_ratecode_cck(bitrate);144144+ }145145+}146146+147147+static u8 bcm43xx_calc_fallback_rate(u8 bitrate)148148+{149149+ switch (bitrate) {150150+ case IEEE80211_CCK_RATE_1MB:151151+ return IEEE80211_CCK_RATE_1MB;152152+ case IEEE80211_CCK_RATE_2MB:153153+ return IEEE80211_CCK_RATE_1MB;154154+ case IEEE80211_CCK_RATE_5MB:155155+ return IEEE80211_CCK_RATE_2MB;156156+ case IEEE80211_CCK_RATE_11MB:157157+ return IEEE80211_CCK_RATE_5MB;158158+ case IEEE80211_OFDM_RATE_6MB:159159+ return IEEE80211_CCK_RATE_5MB;160160+ case IEEE80211_OFDM_RATE_9MB:161161+ return IEEE80211_OFDM_RATE_6MB;162162+ case IEEE80211_OFDM_RATE_12MB:163163+ return IEEE80211_OFDM_RATE_9MB;164164+ case IEEE80211_OFDM_RATE_18MB:165165+ return IEEE80211_OFDM_RATE_12MB;166166+ case IEEE80211_OFDM_RATE_24MB:167167+ return IEEE80211_OFDM_RATE_18MB;168168+ case IEEE80211_OFDM_RATE_36MB:169169+ return IEEE80211_OFDM_RATE_24MB;170170+ case IEEE80211_OFDM_RATE_48MB:171171+ return IEEE80211_OFDM_RATE_36MB;172172+ case IEEE80211_OFDM_RATE_54MB:173173+ return IEEE80211_OFDM_RATE_48MB;174174+ }175175+ assert(0);176176+ return 0;177177+}178178+179179+static180180+__le16 bcm43xx_calc_duration_id(const struct ieee80211_hdr *wireless_header,181181+ u8 bitrate)182182+{183183+ const u16 frame_ctl = le16_to_cpu(wireless_header->frame_ctl);184184+ __le16 duration_id = wireless_header->duration_id;185185+186186+ switch (WLAN_FC_GET_TYPE(frame_ctl)) {187187+ case IEEE80211_FTYPE_DATA:188188+ case IEEE80211_FTYPE_MGMT:189189+ //TODO: Steal the code from ieee80211, once it is completed there.190190+ break;191191+ case IEEE80211_FTYPE_CTL:192192+ /* Use the original duration/id. */193193+ break;194194+ default:195195+ assert(0);196196+ }197197+198198+ return duration_id;199199+}200200+201201+static inline202202+u16 ceiling_div(u16 dividend, u16 divisor)203203+{204204+ return ((dividend + divisor - 1) / divisor);205205+}206206+207207+static void bcm43xx_generate_rts(const struct bcm43xx_phyinfo *phy,208208+ struct bcm43xx_txhdr *txhdr,209209+ u16 *flags,210210+ u8 bitrate,211211+ const struct ieee80211_hdr_4addr *wlhdr)212212+{213213+ u16 fctl;214214+ u16 dur;215215+ u8 fallback_bitrate;216216+ int ofdm_modulation;217217+ int fallback_ofdm_modulation;218218+// u8 *sa, *da;219219+ u16 flen;220220+221221+//FIXME sa = ieee80211_get_SA((struct ieee80211_hdr *)wlhdr);222222+//FIXME da = ieee80211_get_DA((struct ieee80211_hdr *)wlhdr);223223+ fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate);224224+ ofdm_modulation = !(ieee80211_is_cck_rate(bitrate));225225+ fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate));226226+227227+ flen = sizeof(u16) + sizeof(u16) + ETH_ALEN + ETH_ALEN + IEEE80211_FCS_LEN,228228+ bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_plcp),229229+ flen, bitrate,230230+ !ieee80211_is_cck_rate(bitrate));231231+ bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_fallback_plcp),232232+ flen, fallback_bitrate,233233+ !ieee80211_is_cck_rate(fallback_bitrate));234234+ fctl = IEEE80211_FTYPE_CTL;235235+ fctl |= IEEE80211_STYPE_RTS;236236+ dur = le16_to_cpu(wlhdr->duration_id);237237+/*FIXME: should we test for dur==0 here and let it unmodified in this case?238238+ * The following assert checks for this case...239239+ */240240+assert(dur);241241+/*FIXME: The duration calculation is not really correct.242242+ * I am not 100% sure which bitrate to use. We use the RTS rate here,243243+ * but this is likely to be wrong.244244+ */245245+ if (phy->type == BCM43xx_PHYTYPE_A) {246246+ /* Three times SIFS */247247+ dur += 16 * 3;248248+ /* Add ACK duration. */249249+ dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10,250250+ bitrate * 4);251251+ /* Add CTS duration. */252252+ dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10,253253+ bitrate * 4);254254+ } else {255255+ /* Three times SIFS */256256+ dur += 10 * 3;257257+ /* Add ACK duration. */258258+ dur += ceiling_div(8 * (14 /*bytes*/) * 10,259259+ bitrate);260260+ /* Add CTS duration. */261261+ dur += ceiling_div(8 * (14 /*bytes*/) * 10,262262+ bitrate);263263+ }264264+265265+ txhdr->rts_cts_frame_control = cpu_to_le16(fctl);266266+ txhdr->rts_cts_dur = cpu_to_le16(dur);267267+//printk(BCM43xx_MACFMT " " BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(wlhdr->addr1), BCM43xx_MACARG(wlhdr->addr2), BCM43xx_MACARG(wlhdr->addr3));268268+//printk(BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(sa), BCM43xx_MACARG(da));269269+ memcpy(txhdr->rts_cts_mac1, wlhdr->addr1, ETH_ALEN);//FIXME!270270+// memcpy(txhdr->rts_cts_mac2, sa, ETH_ALEN);271271+272272+ *flags |= BCM43xx_TXHDRFLAG_RTSCTS;273273+ *flags |= BCM43xx_TXHDRFLAG_RTS;274274+ if (ofdm_modulation)275275+ *flags |= BCM43xx_TXHDRFLAG_RTSCTS_OFDM;276276+ if (fallback_ofdm_modulation)277277+ *flags |= BCM43xx_TXHDRFLAG_RTSCTSFALLBACK_OFDM;278278+}279279+280280+void bcm43xx_generate_txhdr(struct bcm43xx_private *bcm,281281+ struct bcm43xx_txhdr *txhdr,282282+ const unsigned char *fragment_data,283283+ const unsigned int fragment_len,284284+ const int is_first_fragment,285285+ const u16 cookie)286286+{287287+ const struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);288288+ const struct ieee80211_hdr_4addr *wireless_header = (const struct ieee80211_hdr_4addr *)fragment_data;289289+ const struct ieee80211_security *secinfo = &bcm->ieee->sec;290290+ u8 bitrate;291291+ u8 fallback_bitrate;292292+ int ofdm_modulation;293293+ int fallback_ofdm_modulation;294294+ u16 plcp_fragment_len = fragment_len;295295+ u16 flags = 0;296296+ u16 control = 0;297297+ u16 wsec_rate = 0;298298+ u16 encrypt_frame;299299+300300+ /* Now construct the TX header. */301301+ memset(txhdr, 0, sizeof(*txhdr));302302+303303+ bitrate = bcm->softmac->txrates.default_rate;304304+ ofdm_modulation = !(ieee80211_is_cck_rate(bitrate));305305+ fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate);306306+ fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate));307307+308308+ /* Set Frame Control from 80211 header. */309309+ txhdr->frame_control = wireless_header->frame_ctl;310310+ /* Copy address1 from 80211 header. */311311+ memcpy(txhdr->mac1, wireless_header->addr1, 6);312312+ /* Set the fallback duration ID. */313313+ txhdr->fallback_dur_id = bcm43xx_calc_duration_id((const struct ieee80211_hdr *)wireless_header,314314+ fallback_bitrate);315315+ /* Set the cookie (used as driver internal ID for the frame) */316316+ txhdr->cookie = cpu_to_le16(cookie);317317+318318+ /* Hardware appends FCS. */319319+ plcp_fragment_len += IEEE80211_FCS_LEN;320320+321321+ /* Hardware encryption. */322322+ encrypt_frame = le16_to_cpup(&wireless_header->frame_ctl) & IEEE80211_FCTL_PROTECTED;323323+ if (encrypt_frame && !bcm->ieee->host_encrypt) {324324+ const struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)wireless_header;325325+ memcpy(txhdr->wep_iv, hdr->payload, 4);326326+ /* Hardware appends ICV. */327327+ plcp_fragment_len += 4;328328+329329+ wsec_rate |= (bcm->key[secinfo->active_key].algorithm << BCM43xx_TXHDR_WSEC_ALGO_SHIFT)330330+ & BCM43xx_TXHDR_WSEC_ALGO_MASK;331331+ wsec_rate |= (secinfo->active_key << BCM43xx_TXHDR_WSEC_KEYINDEX_SHIFT)332332+ & BCM43xx_TXHDR_WSEC_KEYINDEX_MASK;333333+ }334334+335335+ /* Generate the PLCP header and the fallback PLCP header. */336336+ bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->plcp),337337+ plcp_fragment_len,338338+ bitrate, ofdm_modulation);339339+ bcm43xx_generate_plcp_hdr(&txhdr->fallback_plcp, plcp_fragment_len,340340+ fallback_bitrate, fallback_ofdm_modulation);341341+342342+ /* Set the CONTROL field */343343+ if (ofdm_modulation)344344+ control |= BCM43xx_TXHDRCTL_OFDM;345345+ if (bcm->short_preamble) //FIXME: could be the other way around, please test346346+ control |= BCM43xx_TXHDRCTL_SHORT_PREAMBLE;347347+ control |= (phy->antenna_diversity << BCM43xx_TXHDRCTL_ANTENNADIV_SHIFT)348348+ & BCM43xx_TXHDRCTL_ANTENNADIV_MASK;349349+350350+ /* Set the FLAGS field */351351+ if (!is_multicast_ether_addr(wireless_header->addr1) &&352352+ !is_broadcast_ether_addr(wireless_header->addr1))353353+ flags |= BCM43xx_TXHDRFLAG_EXPECTACK;354354+ if (1 /* FIXME: PS poll?? */)355355+ flags |= 0x10; // FIXME: unknown meaning.356356+ if (fallback_ofdm_modulation)357357+ flags |= BCM43xx_TXHDRFLAG_FALLBACKOFDM;358358+ if (is_first_fragment)359359+ flags |= BCM43xx_TXHDRFLAG_FIRSTFRAGMENT;360360+361361+ /* Set WSEC/RATE field */362362+ wsec_rate |= (txhdr->plcp.raw[0] << BCM43xx_TXHDR_RATE_SHIFT)363363+ & BCM43xx_TXHDR_RATE_MASK;364364+365365+ /* Generate the RTS/CTS packet, if required. */366366+ /* FIXME: We should first try with CTS-to-self,367367+ * if we are on 80211g. If we get too many368368+ * failures (hidden nodes), we should switch back to RTS/CTS.369369+ */370370+ if (0/*FIXME txctl->use_rts_cts*/) {371371+ bcm43xx_generate_rts(phy, txhdr, &flags,372372+ 0/*FIXME txctl->rts_cts_rate*/,373373+ wireless_header);374374+ }375375+376376+ txhdr->flags = cpu_to_le16(flags);377377+ txhdr->control = cpu_to_le16(control);378378+ txhdr->wsec_rate = cpu_to_le16(wsec_rate);379379+}380380+381381+static s8 bcm43xx_rssi_postprocess(struct bcm43xx_private *bcm,382382+ u8 in_rssi, int ofdm,383383+ int adjust_2053, int adjust_2050)384384+{385385+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);386386+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);387387+ s32 tmp;388388+389389+ switch (radio->version) {390390+ case 0x2050:391391+ if (ofdm) {392392+ tmp = in_rssi;393393+ if (tmp > 127)394394+ tmp -= 256;395395+ tmp *= 73;396396+ tmp /= 64;397397+ if (adjust_2050)398398+ tmp += 25;399399+ else400400+ tmp -= 3;401401+ } else {402402+ if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {403403+ if (in_rssi > 63)404404+ in_rssi = 63;405405+ tmp = radio->nrssi_lt[in_rssi];406406+ tmp = 31 - tmp;407407+ tmp *= -131;408408+ tmp /= 128;409409+ tmp -= 57;410410+ } else {411411+ tmp = in_rssi;412412+ tmp = 31 - tmp;413413+ tmp *= -149;414414+ tmp /= 128;415415+ tmp -= 68;416416+ }417417+ if (phy->type == BCM43xx_PHYTYPE_G &&418418+ adjust_2050)419419+ tmp += 25;420420+ }421421+ break;422422+ case 0x2060:423423+ if (in_rssi > 127)424424+ tmp = in_rssi - 256;425425+ else426426+ tmp = in_rssi;427427+ break;428428+ default:429429+ tmp = in_rssi;430430+ tmp -= 11;431431+ tmp *= 103;432432+ tmp /= 64;433433+ if (adjust_2053)434434+ tmp -= 109;435435+ else436436+ tmp -= 83;437437+ }438438+439439+ return (s8)tmp;440440+}441441+442442+//TODO443443+#if 0444444+static s8 bcm43xx_rssinoise_postprocess(struct bcm43xx_private *bcm,445445+ u8 in_rssi)446446+{447447+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);448448+ s8 ret;449449+450450+ if (phy->type == BCM43xx_PHYTYPE_A) {451451+ //TODO: Incomplete specs.452452+ ret = 0;453453+ } else454454+ ret = bcm43xx_rssi_postprocess(bcm, in_rssi, 0, 1, 1);455455+456456+ return ret;457457+}458458+#endif459459+460460+int bcm43xx_rx(struct bcm43xx_private *bcm,461461+ struct sk_buff *skb,462462+ struct bcm43xx_rxhdr *rxhdr)463463+{464464+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);465465+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);466466+ struct bcm43xx_plcp_hdr4 *plcp;467467+ struct ieee80211_rx_stats stats;468468+ struct ieee80211_hdr_4addr *wlhdr;469469+ u16 frame_ctl;470470+ int is_packet_for_us = 0;471471+ int err = -EINVAL;472472+ const u16 rxflags1 = le16_to_cpu(rxhdr->flags1);473473+ const u16 rxflags2 = le16_to_cpu(rxhdr->flags2);474474+ const u16 rxflags3 = le16_to_cpu(rxhdr->flags3);475475+ const int is_ofdm = !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_OFDM);476476+477477+ if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME) {478478+ plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data + 2);479479+ /* Skip two unknown bytes and the PLCP header. */480480+ skb_pull(skb, 2 + sizeof(struct bcm43xx_plcp_hdr6));481481+ } else {482482+ plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data);483483+ /* Skip the PLCP header. */484484+ skb_pull(skb, sizeof(struct bcm43xx_plcp_hdr6));485485+ }486486+ /* The SKB contains the PAYLOAD (wireless header + data)487487+ * at this point. The FCS at the end is stripped.488488+ */489489+490490+ memset(&stats, 0, sizeof(stats));491491+ stats.mac_time = le16_to_cpu(rxhdr->mactime);492492+ stats.rssi = bcm43xx_rssi_postprocess(bcm, rxhdr->rssi, is_ofdm,493493+ !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_2053RSSIADJ),494494+ !!(rxflags3 & BCM43xx_RXHDR_FLAGS3_2050RSSIADJ));495495+ stats.signal = rxhdr->signal_quality; //FIXME496496+//TODO stats.noise = 497497+ if (is_ofdm)498498+ stats.rate = bcm43xx_plcp_get_bitrate_ofdm(plcp);499499+ else500500+ stats.rate = bcm43xx_plcp_get_bitrate_cck(plcp);501501+//printk("RX ofdm %d, rate == %u\n", is_ofdm, stats.rate);502502+ stats.received_channel = radio->channel;503503+//TODO stats.control = 504504+ stats.mask = IEEE80211_STATMASK_SIGNAL |505505+//TODO IEEE80211_STATMASK_NOISE |506506+ IEEE80211_STATMASK_RATE |507507+ IEEE80211_STATMASK_RSSI;508508+ if (phy->type == BCM43xx_PHYTYPE_A)509509+ stats.freq = IEEE80211_52GHZ_BAND;510510+ else511511+ stats.freq = IEEE80211_24GHZ_BAND;512512+ stats.len = skb->len;513513+514514+ bcm->stats.last_rx = jiffies;515515+ if (bcm->ieee->iw_mode == IW_MODE_MONITOR) {516516+ err = ieee80211_rx(bcm->ieee, skb, &stats);517517+ return (err == 0) ? -EINVAL : 0;518518+ }519519+520520+ wlhdr = (struct ieee80211_hdr_4addr *)(skb->data);521521+522522+ switch (bcm->ieee->iw_mode) {523523+ case IW_MODE_ADHOC:524524+ if (memcmp(wlhdr->addr1, bcm->net_dev->dev_addr, ETH_ALEN) == 0 ||525525+ memcmp(wlhdr->addr3, bcm->ieee->bssid, ETH_ALEN) == 0 ||526526+ is_broadcast_ether_addr(wlhdr->addr1) ||527527+ is_multicast_ether_addr(wlhdr->addr1) ||528528+ bcm->net_dev->flags & IFF_PROMISC)529529+ is_packet_for_us = 1;530530+ break;531531+ case IW_MODE_INFRA:532532+ default:533533+ /* When receiving multicast or broadcast packets, filter out534534+ the packets we send ourself; we shouldn't see those */535535+ if (memcmp(wlhdr->addr3, bcm->ieee->bssid, ETH_ALEN) == 0 ||536536+ memcmp(wlhdr->addr1, bcm->net_dev->dev_addr, ETH_ALEN) == 0 ||537537+ (memcmp(wlhdr->addr3, bcm->net_dev->dev_addr, ETH_ALEN) &&538538+ (is_broadcast_ether_addr(wlhdr->addr1) ||539539+ is_multicast_ether_addr(wlhdr->addr1) ||540540+ bcm->net_dev->flags & IFF_PROMISC)))541541+ is_packet_for_us = 1;542542+ break;543543+ }544544+545545+ frame_ctl = le16_to_cpu(wlhdr->frame_ctl);546546+ if ((frame_ctl & IEEE80211_FCTL_PROTECTED) && !bcm->ieee->host_decrypt) {547547+ frame_ctl &= ~IEEE80211_FCTL_PROTECTED;548548+ wlhdr->frame_ctl = cpu_to_le16(frame_ctl); 549549+ /* trim IV and ICV */550550+ /* FIXME: this must be done only for WEP encrypted packets */551551+ if (skb->len < 32) {552552+ dprintkl(KERN_ERR PFX "RX packet dropped (PROTECTED flag "553553+ "set and length < 32)\n");554554+ return -EINVAL;555555+ } else { 556556+ memmove(skb->data + 4, skb->data, 24);557557+ skb_pull(skb, 4);558558+ skb_trim(skb, skb->len - 4);559559+ stats.len -= 8;560560+ }561561+ wlhdr = (struct ieee80211_hdr_4addr *)(skb->data);562562+ }563563+564564+ switch (WLAN_FC_GET_TYPE(frame_ctl)) {565565+ case IEEE80211_FTYPE_MGMT:566566+ ieee80211_rx_mgt(bcm->ieee, wlhdr, &stats);567567+ break;568568+ case IEEE80211_FTYPE_DATA:569569+ if (is_packet_for_us) {570570+ err = ieee80211_rx(bcm->ieee, skb, &stats);571571+ err = (err == 0) ? -EINVAL : 0;572572+ }573573+ break;574574+ case IEEE80211_FTYPE_CTL:575575+ break;576576+ default:577577+ assert(0);578578+ return -EINVAL;579579+ }580580+581581+ return err;582582+}
···135135 int err = -EINVAL;136136137137 if (in_rate == -1) {138138- /* automatic detect */139139- if (ieee->modulation & IEEE80211_OFDM_MODULATION)140140- in_rate = 54000000;141141- else138138+ /* FIXME: We don't correctly handle backing down to lower139139+ rates, so 801.11g devices start off at 11M for now. People140140+ can manually change it if they really need to, but 11M is141141+ more reliable. Note similar logic in142142+ ieee80211softmac_wx_set_rate() */ 143143+ if (ieee->modulation & IEEE80211_CCK_MODULATION)142144 in_rate = 11000000;145145+ else146146+ in_rate = 54000000;143147 }144148145149 switch (in_rate) {