* [PATCH] bcm43xx-d80211: init routine rewrite
@ 2006-07-10 21:38 Michael Buesch
0 siblings, 0 replies; only message in thread
From: Michael Buesch @ 2006-07-10 21:38 UTC (permalink / raw)
To: John W. Linville; +Cc: bcm43xx-dev, netdev, Francois Barre
Hi John,
Please apply this to wireless-dev.
--
Rewrite of the bcm43xx initialization routines.
This fixes several issues:
* up-down-up-down-up... stale data issue
(May fix some DHCP issues)
* Fix the init vs IRQ handler race (and remove the workaround)
* Fix init for cards with multiple cores (APHY)
The active PHY is selected through d80211.
* Fix the controller restart code.
Controller restart can now also be triggered through
echo 1 > /debug/bcm43xx/ethX/restart
Signed-off-by: Michael Buesch <mb@bu3sch.de>
Index: wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h
===================================================================
--- wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h 2006-07-10 00:41:15.000000000 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h 2006-07-10 00:54:11.000000000 +0200
@@ -493,6 +493,12 @@
* This lock is only used by bcm43xx_phy_{un}lock()
*/
spinlock_t lock;
+
+ /* Firmware. */
+ const struct firmware *ucode;
+ const struct firmware *pcm;
+ const struct firmware *initvals0;
+ const struct firmware *initvals1;
};
@@ -583,12 +589,14 @@
u8 available:1,
enabled:1,
initialized:1;
- /** core_id ID number */
- u16 id;
/** core_rev revision number */
u8 rev;
/** Index number for _switch_core() */
u8 index;
+ /** core_id ID number */
+ u16 id;
+ /** Core-specific data. */
+ void *priv;
};
/* Additional information for each 80211 core. */
@@ -658,7 +666,10 @@
BCM43xx_STAT_RESTARTING, /* controller_restart() called. */
};
#define bcm43xx_status(bcm) atomic_read(&(bcm)->init_status)
-#define bcm43xx_set_status(bcm, stat) atomic_set(&(bcm)->init_status, (stat))
+#define bcm43xx_set_status(bcm, stat) do { \
+ atomic_set(&(bcm)->init_status, (stat)); \
+ smp_wmb(); \
+ } while (0)
/* *** THEORY OF LOCKING ***
*
@@ -740,10 +751,6 @@
struct bcm43xx_coreinfo core_80211[ BCM43xx_MAX_80211_CORES ];
/* Additional information, specific to the 80211 cores. */
struct bcm43xx_coreinfo_80211 core_80211_ext[ BCM43xx_MAX_80211_CORES ];
- /* Index of the current 80211 core. If current_core is not
- * an 80211 core, this is -1.
- */
- int current_80211_core_idx;
/* Number of available 80211 cores. */
int nr_80211_available;
@@ -776,12 +783,6 @@
struct bcm43xx_key key[54];
u8 default_key_idx;
- /* Firmware. */
- const struct firmware *ucode;
- const struct firmware *pcm;
- const struct firmware *initvals0;
- const struct firmware *initvals1;
-
/* Cached beacon template while uploading the template. */
struct sk_buff *cached_beacon;
@@ -828,34 +829,33 @@
* any of these functions.
*/
static inline
+struct bcm43xx_coreinfo_80211 *
+bcm43xx_current_80211_priv(struct bcm43xx_private *bcm)
+{
+ assert(bcm->current_core->id == BCM43xx_COREID_80211);
+ return bcm->current_core->priv;
+}
+static inline
struct bcm43xx_pio * bcm43xx_current_pio(struct bcm43xx_private *bcm)
{
assert(bcm43xx_using_pio(bcm));
- assert(bcm->current_80211_core_idx >= 0);
- assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
- return &(bcm->core_80211_ext[bcm->current_80211_core_idx].pio);
+ return &(bcm43xx_current_80211_priv(bcm)->pio);
}
static inline
struct bcm43xx_dma * bcm43xx_current_dma(struct bcm43xx_private *bcm)
{
assert(!bcm43xx_using_pio(bcm));
- assert(bcm->current_80211_core_idx >= 0);
- assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
- return &(bcm->core_80211_ext[bcm->current_80211_core_idx].dma);
+ return &(bcm43xx_current_80211_priv(bcm)->dma);
}
static inline
struct bcm43xx_phyinfo * bcm43xx_current_phy(struct bcm43xx_private *bcm)
{
- assert(bcm->current_80211_core_idx >= 0);
- assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
- return &(bcm->core_80211_ext[bcm->current_80211_core_idx].phy);
+ return &(bcm43xx_current_80211_priv(bcm)->phy);
}
static inline
struct bcm43xx_radioinfo * bcm43xx_current_radio(struct bcm43xx_private *bcm)
{
- assert(bcm->current_80211_core_idx >= 0);
- assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
- return &(bcm->core_80211_ext[bcm->current_80211_core_idx].radio);
+ return &(bcm43xx_current_80211_priv(bcm)->radio);
}
Index: wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
===================================================================
--- wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c 2006-07-10 00:41:15.000000000 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c 2006-07-10 00:54:11.000000000 +0200
@@ -147,6 +147,73 @@
MODULE_DEVICE_TABLE(pci, bcm43xx_pci_tbl);
+#define RATETAB_ENT(_rateid, _flags) \
+ { \
+ .rate = (((_rateid) * 10) / 2), \
+ .val = (_rateid), \
+ .val2 = (_rateid), \
+ .flags = (_flags), \
+ }
+static const struct ieee80211_rate bcm43xx_cck_ratetable[] = {
+ RATETAB_ENT(BCM43xx_CCK_RATE_1MB, IEEE80211_RATE_CCK),
+ RATETAB_ENT(BCM43xx_CCK_RATE_2MB, IEEE80211_RATE_CCK_2),
+ RATETAB_ENT(BCM43xx_CCK_RATE_5MB, IEEE80211_RATE_CCK_2),
+ RATETAB_ENT(BCM43xx_CCK_RATE_11MB, IEEE80211_RATE_CCK_2),
+};
+static const struct ieee80211_rate bcm43xx_ofdm_ratetable[] = {
+ RATETAB_ENT(BCM43xx_OFDM_RATE_6MB, IEEE80211_RATE_OFDM),
+ RATETAB_ENT(BCM43xx_OFDM_RATE_9MB, IEEE80211_RATE_OFDM),
+ RATETAB_ENT(BCM43xx_OFDM_RATE_12MB, IEEE80211_RATE_OFDM),
+ RATETAB_ENT(BCM43xx_OFDM_RATE_18MB, IEEE80211_RATE_OFDM),
+ RATETAB_ENT(BCM43xx_OFDM_RATE_24MB, IEEE80211_RATE_OFDM),
+ RATETAB_ENT(BCM43xx_OFDM_RATE_36MB, IEEE80211_RATE_OFDM),
+ RATETAB_ENT(BCM43xx_OFDM_RATE_48MB, IEEE80211_RATE_OFDM),
+ RATETAB_ENT(BCM43xx_OFDM_RATE_54MB, IEEE80211_RATE_OFDM),
+};
+#define CHANTAB_ENT(_chanid, _freq) \
+ { \
+ .chan = (_chanid), \
+ .freq = (_freq), \
+ .val = (_chanid), \
+ .flag = IEEE80211_CHAN_W_SCAN | \
+ IEEE80211_CHAN_W_ACTIVE_SCAN | \
+ IEEE80211_CHAN_W_IBSS, \
+ .power_level = 0xFF, \
+ .antenna_max = 0xFF, \
+ }
+static const struct ieee80211_channel bcm43xx_bg_chantable[] = {
+ CHANTAB_ENT(1, 2412),
+ CHANTAB_ENT(2, 2417),
+ CHANTAB_ENT(3, 2422),
+ CHANTAB_ENT(4, 2427),
+ CHANTAB_ENT(5, 2432),
+ CHANTAB_ENT(6, 2437),
+ CHANTAB_ENT(7, 2442),
+ CHANTAB_ENT(8, 2447),
+ CHANTAB_ENT(9, 2452),
+ CHANTAB_ENT(10, 2457),
+ CHANTAB_ENT(11, 2462),
+ CHANTAB_ENT(12, 2467),
+ CHANTAB_ENT(13, 2472),
+ CHANTAB_ENT(14, 2484),
+};
+static const struct ieee80211_channel bcm43xx_a_chantable[] = {
+ CHANTAB_ENT(36, 5180),
+ CHANTAB_ENT(40, 5200),
+ CHANTAB_ENT(44, 5220),
+ CHANTAB_ENT(48, 5240),
+ CHANTAB_ENT(52, 5260),
+ CHANTAB_ENT(56, 5280),
+ CHANTAB_ENT(60, 5300),
+ CHANTAB_ENT(64, 5320),
+ CHANTAB_ENT(149, 5745),
+ CHANTAB_ENT(153, 5765),
+ CHANTAB_ENT(157, 5785),
+ CHANTAB_ENT(161, 5805),
+ CHANTAB_ENT(165, 5825),
+};
+
+
static void bcm43xx_ram_write(struct bcm43xx_private *bcm, u16 offset, u32 val)
{
u32 status;
@@ -516,23 +583,19 @@
}
/* Make sure we don't receive more data from the device. */
-static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate)
+static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm)
{
unsigned long flags;
- u32 old;
spin_lock_irqsave(&bcm->irq_lock, flags);
if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) {
spin_unlock_irqrestore(&bcm->irq_lock, flags);
return -EBUSY;
}
- old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
spin_unlock_irqrestore(&bcm->irq_lock, flags);
bcm43xx_synchronize_irq(bcm);
- if (oldstate)
- *oldstate = old;
-
return 0;
}
@@ -544,7 +607,6 @@
u16 manufact;
u16 version;
u8 revision;
- s8 i;
if (bcm->chip_id == 0x4317) {
if (bcm->chip_rev == 0x00)
@@ -587,17 +649,6 @@
radio->version = version;
radio->revision = revision;
- /* Set default attenuation values. */
- radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm);
- radio->radio_atten = bcm43xx_default_radio_attenuation(bcm);
- radio->txctl1 = bcm43xx_default_txctl1(bcm);
- radio->txctl2 = 0xFFFF;
- radio->power_level = ~0;
-
- /* Initialize the in-memory nrssi Lookup Table. */
- for (i = 0; i < 64; i++)
- radio->nrssi_lt[i] = i;
-
return 0;
err_unsupported_radio:
@@ -1150,10 +1201,6 @@
goto out;
bcm->current_core = new_core;
- bcm->current_80211_core_idx = -1;
- if (new_core->id == BCM43xx_COREID_80211)
- bcm->current_80211_core_idx = (int)(new_core - &(bcm->core_80211[0]));
-
out:
return err;
}
@@ -1323,43 +1370,23 @@
bcm43xx_core_disable(bcm, 0);
}
-/* Mark the current 80211 core inactive.
- * "active_80211_core" is the other 80211 core, which is used.
- */
-static int bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm,
- struct bcm43xx_coreinfo *active_80211_core)
+/* Mark the current 80211 core inactive. */
+static void bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm)
{
u32 sbtmstatelow;
- struct bcm43xx_coreinfo *old_core;
- int err = 0;
bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
bcm43xx_radio_turn_off(bcm);
sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
- sbtmstatelow &= ~0x200a0000;
- sbtmstatelow |= 0xa0000;
+ sbtmstatelow &= 0xDFF5FFFF;
+ sbtmstatelow |= 0x000A0000;
bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
udelay(1);
sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
- sbtmstatelow &= ~0xa0000;
- sbtmstatelow |= 0x80000;
+ sbtmstatelow &= 0xFFF5FFFF;
+ sbtmstatelow |= 0x00080000;
bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
udelay(1);
-
- if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) {
- old_core = bcm->current_core;
- err = bcm43xx_switch_core(bcm, active_80211_core);
- if (err)
- goto out;
- sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
- sbtmstatelow &= ~0x20000000;
- sbtmstatelow |= 0x20000000;
- bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
- err = bcm43xx_switch_core(bcm, old_core);
- }
-
-out:
- return err;
}
static void handle_irq_transmit_status(struct bcm43xx_private *bcm)
@@ -2008,6 +2035,9 @@
spin_lock(&bcm->irq_lock);
+ assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
+ assert(bcm->current_core->id == BCM43xx_COREID_80211);
+
reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
if (reason == 0xffffffff) {
/* irq not for us (shared irq) */
@@ -2028,21 +2058,11 @@
& 0x0001dc00;
bcm43xx_interrupt_ack(bcm, reason);
-
- /* Only accept IRQs, if we are initialized properly.
- * This avoids an RX race while initializing.
- * We should probably not enable IRQs before we are initialized
- * completely, but some careful work is needed to fix this. I think it
- * is best to stay with this cheap workaround for now... .
- */
- if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) {
- /* disable all IRQs. They are enabled again in the bottom half. */
- bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
- /* save the reason code and call our bottom half. */
- bcm->irq_reason = reason;
- tasklet_schedule(&bcm->isr_tasklet);
- }
-
+ /* disable all IRQs. They are enabled again in the bottom half. */
+ bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+ /* save the reason code and call our bottom half. */
+ bcm->irq_reason = reason;
+ tasklet_schedule(&bcm->isr_tasklet);
out:
mmiowb();
spin_unlock(&bcm->irq_lock);
@@ -2052,16 +2072,18 @@
static void bcm43xx_release_firmware(struct bcm43xx_private *bcm, int force)
{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+
if (bcm->firmware_norelease && !force)
return; /* Suspending or controller reset. */
- release_firmware(bcm->ucode);
- bcm->ucode = NULL;
- release_firmware(bcm->pcm);
- bcm->pcm = NULL;
- release_firmware(bcm->initvals0);
- bcm->initvals0 = NULL;
- release_firmware(bcm->initvals1);
- bcm->initvals1 = NULL;
+ release_firmware(phy->ucode);
+ phy->ucode = NULL;
+ release_firmware(phy->pcm);
+ phy->pcm = NULL;
+ release_firmware(phy->initvals0);
+ phy->initvals0 = NULL;
+ release_firmware(phy->initvals1);
+ phy->initvals1 = NULL;
}
static int bcm43xx_request_firmware(struct bcm43xx_private *bcm)
@@ -2072,11 +2094,11 @@
int nr;
char buf[22 + sizeof(modparam_fwpostfix) - 1] = { 0 };
- if (!bcm->ucode) {
+ if (!phy->ucode) {
snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_microcode%d%s.fw",
(rev >= 5 ? 5 : rev),
modparam_fwpostfix);
- err = request_firmware(&bcm->ucode, buf, &bcm->pci_dev->dev);
+ err = request_firmware(&phy->ucode, buf, &bcm->pci_dev->dev);
if (err) {
printk(KERN_ERR PFX
"Error: Microcode \"%s\" not available or load failed.\n",
@@ -2085,12 +2107,12 @@
}
}
- if (!bcm->pcm) {
+ if (!phy->pcm) {
snprintf(buf, ARRAY_SIZE(buf),
"bcm43xx_pcm%d%s.fw",
(rev < 5 ? 4 : 5),
modparam_fwpostfix);
- err = request_firmware(&bcm->pcm, buf, &bcm->pci_dev->dev);
+ err = request_firmware(&phy->pcm, buf, &bcm->pci_dev->dev);
if (err) {
printk(KERN_ERR PFX
"Error: PCM \"%s\" not available or load failed.\n",
@@ -2099,7 +2121,7 @@
}
}
- if (!bcm->initvals0) {
+ if (!phy->initvals0) {
if (rev == 2 || rev == 4) {
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
@@ -2130,20 +2152,20 @@
snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw",
nr, modparam_fwpostfix);
- err = request_firmware(&bcm->initvals0, buf, &bcm->pci_dev->dev);
+ err = request_firmware(&phy->initvals0, buf, &bcm->pci_dev->dev);
if (err) {
printk(KERN_ERR PFX
"Error: InitVals \"%s\" not available or load failed.\n",
buf);
goto error;
}
- if (bcm->initvals0->size % sizeof(struct bcm43xx_initval)) {
+ if (phy->initvals0->size % sizeof(struct bcm43xx_initval)) {
printk(KERN_ERR PFX "InitVals fileformat error.\n");
goto error;
}
}
- if (!bcm->initvals1) {
+ if (!phy->initvals1) {
if (rev >= 5) {
u32 sbtmstatehigh;
@@ -2165,14 +2187,14 @@
snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw",
nr, modparam_fwpostfix);
- err = request_firmware(&bcm->initvals1, buf, &bcm->pci_dev->dev);
+ err = request_firmware(&phy->initvals1, buf, &bcm->pci_dev->dev);
if (err) {
printk(KERN_ERR PFX
"Error: InitVals \"%s\" not available or load failed.\n",
buf);
goto error;
}
- if (bcm->initvals1->size % sizeof(struct bcm43xx_initval)) {
+ if (phy->initvals1->size % sizeof(struct bcm43xx_initval)) {
printk(KERN_ERR PFX "InitVals fileformat error.\n");
goto error;
}
@@ -2192,12 +2214,13 @@
static void bcm43xx_upload_microcode(struct bcm43xx_private *bcm)
{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
const u32 *data;
unsigned int i, len;
/* Upload Microcode. */
- data = (u32 *)(bcm->ucode->data);
- len = bcm->ucode->size / sizeof(u32);
+ data = (u32 *)(phy->ucode->data);
+ len = phy->ucode->size / sizeof(u32);
bcm43xx_shm_control_word(bcm, BCM43xx_SHM_UCODE, 0x0000);
for (i = 0; i < len; i++) {
bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA,
@@ -2206,8 +2229,8 @@
}
/* Upload PCM data. */
- data = (u32 *)(bcm->pcm->data);
- len = bcm->pcm->size / sizeof(u32);
+ data = (u32 *)(phy->pcm->data);
+ len = phy->pcm->size / sizeof(u32);
bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01ea);
bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, 0x00004000);
bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01eb);
@@ -2253,15 +2276,16 @@
static int bcm43xx_upload_initvals(struct bcm43xx_private *bcm)
{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
int err;
- err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals0->data,
- bcm->initvals0->size / sizeof(struct bcm43xx_initval));
+ err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)phy->initvals0->data,
+ phy->initvals0->size / sizeof(struct bcm43xx_initval));
if (err)
goto out;
- if (bcm->initvals1) {
- err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals1->data,
- bcm->initvals1->size / sizeof(struct bcm43xx_initval));
+ if (phy->initvals1) {
+ err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)phy->initvals1->data,
+ phy->initvals1->size / sizeof(struct bcm43xx_initval));
if (err)
goto out;
}
@@ -2271,9 +2295,7 @@
static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm)
{
- int res;
- unsigned int i;
- u32 data;
+ int err;
bcm->irq = bcm->pci_dev->irq;
#ifdef CONFIG_BCM947XX
@@ -2286,32 +2308,12 @@
}
}
#endif
- res = request_irq(bcm->irq, bcm43xx_interrupt_handler,
+ err = request_irq(bcm->irq, bcm43xx_interrupt_handler,
SA_SHIRQ, KBUILD_MODNAME, bcm);
- if (res) {
+ if (err)
printk(KERN_ERR PFX "Cannot register IRQ%d\n", bcm->irq);
- return -ENODEV;
- }
- bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xffffffff);
- bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402);
- i = 0;
- while (1) {
- data = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
- if (data == BCM43xx_IRQ_READY)
- break;
- i++;
- if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) {
- printk(KERN_ERR PFX "Card IRQ register not responding. "
- "Giving up.\n");
- free_irq(bcm->irq, bcm);
- return -ENODEV;
- }
- udelay(10);
- }
- // dummy read
- bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
- return 0;
+ return err;
}
/* Switch to the core used to write the GPIO register.
@@ -2498,6 +2500,46 @@
bcm43xx_write16(bcm, 0x0612, value);
}
+static void bcm43xx_rate_memory_write(struct bcm43xx_private *bcm,
+ u16 rate,
+ int is_ofdm)
+{
+ u16 offset;
+
+ if (is_ofdm) {
+ offset = 0x480;
+ offset += (bcm43xx_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2;
+ } else {
+ offset = 0x4C0;
+ offset += (bcm43xx_plcp_get_ratecode_cck(rate) & 0x000F) * 2;
+ }
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, offset + 0x20,
+ bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, offset));
+}
+
+static void bcm43xx_rate_memory_init(struct bcm43xx_private *bcm)
+{
+ switch (bcm43xx_current_phy(bcm)->type) {
+ case BCM43xx_PHYTYPE_A:
+ case BCM43xx_PHYTYPE_G:
+ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_6MB, 1);
+ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_12MB, 1);
+ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_18MB, 1);
+ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_24MB, 1);
+ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_36MB, 1);
+ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_48MB, 1);
+ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_54MB, 1);
+ case BCM43xx_PHYTYPE_B:
+ bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_1MB, 0);
+ bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_2MB, 0);
+ bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_5MB, 0);
+ bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_11MB, 0);
+ break;
+ default:
+ assert(0);
+ }
+}
+
/* This is the opposite of bcm43xx_chip_init() */
static void bcm43xx_chip_cleanup(struct bcm43xx_private *bcm)
{
@@ -2505,7 +2547,6 @@
if (!modparam_noleds)
bcm43xx_leds_exit(bcm);
bcm43xx_gpio_cleanup(bcm);
- free_irq(bcm->irq, bcm);
bcm43xx_release_firmware(bcm, 0);
}
@@ -2517,7 +2558,7 @@
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
int err;
- int tmp;
+ int i, tmp;
u32 value32;
u16 value16;
@@ -2529,12 +2570,27 @@
if (err)
goto out;
bcm43xx_upload_microcode(bcm);
- err = bcm43xx_initialize_irq(bcm);
- if (err)
- goto err_release_fw;
+
+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xFFFFFFFF);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402);
+ i = 0;
+ while (1) {
+ value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
+ if (value32 == BCM43xx_IRQ_READY)
+ break;
+ i++;
+ if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) {
+ printk(KERN_ERR PFX "IRQ_READY timeout\n");
+ err = -ENODEV;
+ goto err_release_fw;
+ }
+ udelay(10);
+ }
+ bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */
+
err = bcm43xx_gpio_init(bcm);
if (err)
- goto err_free_irq;
+ goto err_release_fw;
err = bcm43xx_upload_initvals(bcm);
if (err)
goto err_gpio_cleanup;
@@ -2617,140 +2673,457 @@
bcm43xx_radio_turn_off(bcm);
err_gpio_cleanup:
bcm43xx_gpio_cleanup(bcm);
-err_free_irq:
- free_irq(bcm->irq, bcm);
err_release_fw:
bcm43xx_release_firmware(bcm, 1);
goto out;
}
-
-/* Validate chip access
- * http://bcm-specs.sipsolutions.net/ValidateChipAccess */
-static int bcm43xx_validate_chip(struct bcm43xx_private *bcm)
-{
- u32 value;
- u32 shm_backup;
-
- shm_backup = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000);
- bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0xAA5555AA);
- if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0xAA5555AA)
- goto error;
- bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0x55AAAA55);
- if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0x55AAAA55)
- goto error;
- bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, shm_backup);
-
- value = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
- if ((value | 0x80000000) != 0x80000400)
- goto error;
-
- value = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
- if (value != 0x00000000)
- goto error;
-
- return 0;
-error:
- printk(KERN_ERR PFX "Failed to validate the chipaccess\n");
- return -ENODEV;
-}
-static void bcm43xx_init_struct_phyinfo(struct bcm43xx_phyinfo *phy)
+static void bcm43xx_wireless_core_cleanup(struct bcm43xx_private *bcm)
{
- /* Initialize a "phyinfo" structure. The structure is already
- * zeroed out.
- */
- phy->antenna_diversity = 0xFFFF;
- phy->savedpctlreg = 0xFFFF;
- phy->minlowsig[0] = 0xFFFF;
- phy->minlowsig[1] = 0xFFFF;
- spin_lock_init(&phy->lock);
-}
+ bcm43xx_chip_cleanup(bcm);
+ bcm43xx_pio_free(bcm);
+ bcm43xx_dma_free(bcm);
-static void bcm43xx_init_struct_radioinfo(struct bcm43xx_radioinfo *radio)
-{
- /* Initialize a "radioinfo" structure. The structure is already
- * zeroed out.
- */
- radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE;
- radio->channel = 0xFF;
- radio->initial_channel = 0xFF;
- radio->lofcal = 0xFFFF;
- radio->initval = 0xFFFF;
- radio->nrssi[0] = -1000;
- radio->nrssi[1] = -1000;
+ bcm->current_core->initialized = 0;
}
-static int bcm43xx_probe_cores(struct bcm43xx_private *bcm)
+/* http://bcm-specs.sipsolutions.net/80211Init */
+static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm,
+ int active_wlcore)
{
- int err, i;
- int current_core;
- u32 core_vendor, core_id, core_rev;
- u32 sb_id_hi, chip_id_32 = 0;
- u16 pci_device, chip_id_16;
- u8 core_count;
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u32 ucodeflags;
+ int err;
+ u32 sbimconfiglow;
+ u8 limit;
- memset(&bcm->core_chipcommon, 0, sizeof(struct bcm43xx_coreinfo));
- memset(&bcm->core_pci, 0, sizeof(struct bcm43xx_coreinfo));
- memset(&bcm->core_80211, 0, sizeof(struct bcm43xx_coreinfo)
- * BCM43xx_MAX_80211_CORES);
- memset(&bcm->core_80211_ext, 0, sizeof(struct bcm43xx_coreinfo_80211)
- * BCM43xx_MAX_80211_CORES);
- bcm->current_80211_core_idx = -1;
- bcm->nr_80211_available = 0;
- bcm->current_core = NULL;
- bcm->active_80211_core = NULL;
+ if (bcm->chip_rev < 5) {
+ sbimconfiglow = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW);
+ sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK;
+ sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK;
+ if (bcm->bustype == BCM43xx_BUSTYPE_PCI)
+ sbimconfiglow |= 0x32;
+ else if (bcm->bustype == BCM43xx_BUSTYPE_SB)
+ sbimconfiglow |= 0x53;
+ else
+ assert(0);
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, sbimconfiglow);
+ }
- /* map core 0 */
- err = _switch_core(bcm, 0);
+ bcm43xx_phy_calibrate(bcm);
+ err = bcm43xx_chip_init(bcm);
if (err)
goto out;
- /* fetch sb_id_hi from core information registers */
- sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0016, bcm->current_core->rev);
+ ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, BCM43xx_UCODEFLAGS_OFFSET);
- core_id = (sb_id_hi & 0xFFF0) >> 4;
- core_rev = (sb_id_hi & 0xF);
- core_vendor = (sb_id_hi & 0xFFFF0000) >> 16;
+ if (0 /*FIXME: which condition has to be used here? */)
+ ucodeflags |= 0x00000010;
- /* if present, chipcommon is always core 0; read the chipid from it */
- if (core_id == BCM43xx_COREID_CHIPCOMMON) {
- chip_id_32 = bcm43xx_read32(bcm, 0);
- chip_id_16 = chip_id_32 & 0xFFFF;
- bcm->core_chipcommon.available = 1;
- bcm->core_chipcommon.id = core_id;
- bcm->core_chipcommon.rev = core_rev;
- bcm->core_chipcommon.index = 0;
- /* While we are at it, also read the capabilities. */
- bcm->chipcommon_capabilities = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_CAPABILITIES);
- } else {
- /* without a chipCommon, use a hard coded table. */
- pci_device = bcm->pci_dev->device;
- if (pci_device == 0x4301)
- chip_id_16 = 0x4301;
- else if ((pci_device >= 0x4305) && (pci_device <= 0x4307))
- chip_id_16 = 0x4307;
- else if ((pci_device >= 0x4402) && (pci_device <= 0x4403))
- chip_id_16 = 0x4402;
- else if ((pci_device >= 0x4610) && (pci_device <= 0x4615))
- chip_id_16 = 0x4610;
- else if ((pci_device >= 0x4710) && (pci_device <= 0x4715))
- chip_id_16 = 0x4710;
-#ifdef CONFIG_BCM947XX
- else if ((pci_device >= 0x4320) && (pci_device <= 0x4325))
- chip_id_16 = 0x4309;
-#endif
- else {
- printk(KERN_ERR PFX "Could not determine Chip ID\n");
- return -ENODEV;
- }
- }
+ /* HW decryption needs to be set now. */
+ ucodeflags |= 0x40000000;
- /* ChipCommon with Core Rev >=4 encodes number of cores,
- * otherwise consult hardcoded table */
- if ((core_id == BCM43xx_COREID_CHIPCOMMON) && (core_rev >= 4)) {
- core_count = (chip_id_32 & 0x0F000000) >> 24;
- } else {
- switch (chip_id_16) {
+ if (phy->type == BCM43xx_PHYTYPE_G) {
+ ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY;
+ if (phy->rev == 1)
+ ucodeflags |= BCM43xx_UCODEFLAG_UNKGPHY;
+ if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)
+ ucodeflags |= BCM43xx_UCODEFLAG_UNKPACTRL;
+ } else if (phy->type == BCM43xx_PHYTYPE_B) {
+ ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY;
+ if (phy->rev >= 2 && radio->version == 0x2050)
+ ucodeflags &= ~BCM43xx_UCODEFLAG_UNKGPHY;
+ }
+
+ if (ucodeflags != bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET)) {
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET, ucodeflags);
+ }
+
+ /* Short/Long Retry Limit.
+ * The retry-limit is a 4-bit counter. Enforce this to avoid overflowing
+ * the chip-internal counter.
+ */
+ limit = limit_value(modparam_short_retry, 0, 0xF);
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0006, limit);
+ limit = limit_value(modparam_long_retry, 0, 0xF);
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0007, limit);
+
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0044, 3);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0046, 2);
+
+ bcm43xx_rate_memory_init(bcm);
+
+ /* Minimum Contention Window */
+ if (phy->type == BCM43xx_PHYTYPE_B)
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000001f);
+ else
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000000f);
+ /* Maximum Contention Window */
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff);
+
+ bcm43xx_write_mac_bssid_templates(bcm);
+
+ if (bcm->current_core->rev >= 5)
+ bcm43xx_write16(bcm, 0x043C, 0x000C);
+
+ if (active_wlcore) {
+ if (bcm43xx_using_pio(bcm))
+ err = bcm43xx_pio_init(bcm);
+ else
+ err = bcm43xx_dma_init(bcm);
+ if (err)
+ goto err_chip_cleanup;
+ }
+ bcm43xx_write16(bcm, 0x0612, 0x0050);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0416, 0x0050);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0414, 0x01F4);
+
+ if (active_wlcore) {
+ if (radio->initial_channel != 0xFF)
+ bcm43xx_radio_selectchannel(bcm, radio->initial_channel, 0);
+ }
+
+ /* Don't enable MAC/IRQ here, as it will race with the IRQ handler.
+ * We enable it later.
+ */
+ bcm->current_core->initialized = 1;
+out:
+ return err;
+
+err_chip_cleanup:
+ bcm43xx_chip_cleanup(bcm);
+ goto out;
+}
+
+static void bcm43xx_periodic_every120sec(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+
+ if (phy->type != BCM43xx_PHYTYPE_G || phy->rev < 2)
+ return;
+
+ bcm43xx_mac_suspend(bcm);
+ bcm43xx_phy_lo_g_measure(bcm);
+ bcm43xx_mac_enable(bcm);
+}
+
+static void bcm43xx_periodic_every60sec(struct bcm43xx_private *bcm)
+{
+ bcm43xx_phy_lo_mark_all_unused(bcm);
+ if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {
+ bcm43xx_mac_suspend(bcm);
+ bcm43xx_calc_nrssi_slope(bcm);
+ bcm43xx_mac_enable(bcm);
+ }
+}
+
+static void bcm43xx_periodic_every30sec(struct bcm43xx_private *bcm)
+{
+ /* Update device statistics. */
+ bcm43xx_calculate_link_quality(bcm);
+}
+
+static void bcm43xx_periodic_every15sec(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+
+ if (phy->type == BCM43xx_PHYTYPE_G) {
+ //TODO: update_aci_moving_average
+ if (radio->aci_enable && radio->aci_wlan_automatic) {
+ bcm43xx_mac_suspend(bcm);
+ if (!radio->aci_enable && 1 /*TODO: not scanning? */) {
+ if (0 /*TODO: bunch of conditions*/) {
+ bcm43xx_radio_set_interference_mitigation(bcm,
+ BCM43xx_RADIO_INTERFMODE_MANUALWLAN);
+ }
+ } else if (1/*TODO*/) {
+ /*
+ if ((aci_average > 1000) && !(bcm43xx_radio_aci_scan(bcm))) {
+ bcm43xx_radio_set_interference_mitigation(bcm,
+ BCM43xx_RADIO_INTERFMODE_NONE);
+ }
+ */
+ }
+ bcm43xx_mac_enable(bcm);
+ } else if (radio->interfmode == BCM43xx_RADIO_INTERFMODE_NONWLAN &&
+ phy->rev == 1) {
+ //TODO: implement rev1 workaround
+ }
+ }
+ bcm43xx_phy_xmitpower(bcm); //FIXME: unless scanning?
+ //TODO for APHY (temperature?)
+}
+
+static void do_periodic_work(struct bcm43xx_private *bcm)
+{
+ unsigned int state;
+
+ state = bcm->periodic_state;
+ if (state % 8 == 0)
+ bcm43xx_periodic_every120sec(bcm);
+ if (state % 4 == 0)
+ bcm43xx_periodic_every60sec(bcm);
+ if (state % 2 == 0)
+ bcm43xx_periodic_every30sec(bcm);
+ if (state % 1 == 0)
+ bcm43xx_periodic_every15sec(bcm);
+ bcm->periodic_state = state + 1;
+
+ schedule_delayed_work(&bcm->periodic_work, HZ * 15);
+}
+
+/* Estimate a "Badness" value based on the periodic work
+ * state-machine state. "Badness" is worse (bigger), if the
+ * periodic work will take longer.
+ */
+static int estimate_periodic_work_badness(unsigned int state)
+{
+ int badness = 0;
+
+ if (state % 8 == 0) /* every 120 sec */
+ badness += 10;
+ if (state % 4 == 0) /* every 60 sec */
+ badness += 5;
+ if (state % 2 == 0) /* every 30 sec */
+ badness += 1;
+ if (state % 1 == 0) /* every 15 sec */
+ badness += 1;
+
+#define BADNESS_LIMIT 4
+ return badness;
+}
+
+static void bcm43xx_periodic_work_handler(void *d)
+{
+ struct bcm43xx_private *bcm = d;
+ unsigned long flags;
+ u32 savedirqs = 0;
+ int badness;
+
+ badness = estimate_periodic_work_badness(bcm->periodic_state);
+ if (badness > BADNESS_LIMIT) {
+ /* Periodic work will take a long time, so we want it to
+ * be preemtible.
+ */
+ netif_stop_queue(bcm->net_dev);
+ synchronize_net();
+ spin_lock_irqsave(&bcm->irq_lock, flags);
+ bcm43xx_mac_suspend(bcm);
+ if (bcm43xx_using_pio(bcm))
+ bcm43xx_pio_freeze_txqueues(bcm);
+ savedirqs = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+ spin_unlock_irqrestore(&bcm->irq_lock, flags);
+ mutex_lock(&bcm->mutex);
+ bcm43xx_synchronize_irq(bcm);
+ } else {
+ /* Periodic work should take short time, so we want low
+ * locking overhead.
+ */
+ mutex_lock(&bcm->mutex);
+ spin_lock_irqsave(&bcm->irq_lock, flags);
+ }
+
+ do_periodic_work(bcm);
+
+ if (badness > BADNESS_LIMIT) {
+ spin_lock_irqsave(&bcm->irq_lock, flags);
+ if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) {
+ tasklet_enable(&bcm->isr_tasklet);
+ bcm43xx_interrupt_enable(bcm, savedirqs);
+ if (bcm43xx_using_pio(bcm))
+ bcm43xx_pio_thaw_txqueues(bcm);
+ bcm43xx_mac_enable(bcm);
+ }
+ netif_wake_queue(bcm->net_dev);
+ }
+ mmiowb();
+ spin_unlock_irqrestore(&bcm->irq_lock, flags);
+ mutex_unlock(&bcm->mutex);
+}
+
+static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm)
+{
+ cancel_rearming_delayed_work(&bcm->periodic_work);
+}
+
+static void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm)
+{
+ struct work_struct *work = &(bcm->periodic_work);
+
+ assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
+ INIT_WORK(work, bcm43xx_periodic_work_handler, bcm);
+ schedule_work(work);
+}
+
+static int bcm43xx_shutdown_all_wireless_cores(struct bcm43xx_private *bcm)
+{
+ int ret = 0;
+ int i, err;
+ struct bcm43xx_coreinfo *core;
+
+ bcm43xx_set_status(bcm, BCM43xx_STAT_SHUTTINGDOWN);
+ for (i = 0; i < bcm->nr_80211_available; i++) {
+ core = &(bcm->core_80211[i]);
+ assert(core->available);
+ if (!core->initialized)
+ continue;
+ err = bcm43xx_switch_core(bcm, core);
+ if (err) {
+ dprintk(KERN_ERR PFX "shutdown_all_wireless_cores "
+ "switch_core failed (%d)\n", err);
+ ret = err;
+ continue;
+ }
+ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+ bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */
+ bcm43xx_wireless_core_cleanup(bcm);
+ if (core == bcm->active_80211_core)
+ bcm->active_80211_core = NULL;
+ }
+ free_irq(bcm->irq, bcm);
+ bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT);
+
+ return ret;
+}
+
+/* This is the opposite of bcm43xx_init_board() */
+static void bcm43xx_free_board(struct bcm43xx_private *bcm)
+{
+ bcm43xx_sysfs_unregister(bcm);
+ bcm43xx_periodic_tasks_delete(bcm);
+
+ mutex_lock(&bcm->mutex);
+ bcm43xx_shutdown_all_wireless_cores(bcm);
+ bcm43xx_pctl_set_crystal(bcm, 0);
+ mutex_unlock(&bcm->mutex);
+}
+
+/* Validate chip access
+ * http://bcm-specs.sipsolutions.net/ValidateChipAccess */
+static int bcm43xx_validate_chip(struct bcm43xx_private *bcm)
+{
+ u32 value;
+ u32 shm_backup;
+
+ shm_backup = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000);
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0xAA5555AA);
+ if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0xAA5555AA)
+ goto error;
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0x55AAAA55);
+ if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0x55AAAA55)
+ goto error;
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, shm_backup);
+
+ value = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ if ((value | 0x80000000) != 0x80000400)
+ goto error;
+
+ value = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
+ if (value != 0x00000000)
+ goto error;
+
+ return 0;
+error:
+ printk(KERN_ERR PFX "Failed to validate the chipaccess\n");
+ return -ENODEV;
+}
+
+static void bcm43xx_init_struct_phyinfo(struct bcm43xx_phyinfo *phy)
+{
+ /* Initialize a "phyinfo" structure. The structure is already
+ * zeroed out.
+ * This is called on insmod time to initialize members.
+ */
+ phy->savedpctlreg = 0xFFFF;
+ spin_lock_init(&phy->lock);
+}
+
+static void bcm43xx_init_struct_radioinfo(struct bcm43xx_radioinfo *radio)
+{
+ /* Initialize a "radioinfo" structure. The structure is already
+ * zeroed out.
+ * This is called on insmod time to initialize members.
+ */
+ radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE;
+ radio->channel = 0xFF;
+ radio->initial_channel = 0xFF;
+}
+
+static int bcm43xx_probe_cores(struct bcm43xx_private *bcm)
+{
+ int err, i;
+ int current_core;
+ u32 core_vendor, core_id, core_rev;
+ u32 sb_id_hi, chip_id_32 = 0;
+ u16 pci_device, chip_id_16;
+ u8 core_count;
+
+ memset(&bcm->core_chipcommon, 0, sizeof(struct bcm43xx_coreinfo));
+ memset(&bcm->core_pci, 0, sizeof(struct bcm43xx_coreinfo));
+ memset(&bcm->core_80211, 0, sizeof(struct bcm43xx_coreinfo)
+ * BCM43xx_MAX_80211_CORES);
+ memset(&bcm->core_80211_ext, 0, sizeof(struct bcm43xx_coreinfo_80211)
+ * BCM43xx_MAX_80211_CORES);
+ bcm->nr_80211_available = 0;
+ bcm->current_core = NULL;
+ bcm->active_80211_core = NULL;
+
+ /* map core 0 */
+ err = _switch_core(bcm, 0);
+ if (err)
+ goto out;
+
+ /* fetch sb_id_hi from core information registers */
+ sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI);
+
+ core_id = (sb_id_hi & 0xFFF0) >> 4;
+ core_rev = (sb_id_hi & 0xF);
+ core_vendor = (sb_id_hi & 0xFFFF0000) >> 16;
+
+ /* if present, chipcommon is always core 0; read the chipid from it */
+ if (core_id == BCM43xx_COREID_CHIPCOMMON) {
+ chip_id_32 = bcm43xx_read32(bcm, 0);
+ chip_id_16 = chip_id_32 & 0xFFFF;
+ bcm->core_chipcommon.available = 1;
+ bcm->core_chipcommon.id = core_id;
+ bcm->core_chipcommon.rev = core_rev;
+ bcm->core_chipcommon.index = 0;
+ /* While we are at it, also read the capabilities. */
+ bcm->chipcommon_capabilities = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_CAPABILITIES);
+ } else {
+ /* without a chipCommon, use a hard coded table. */
+ pci_device = bcm->pci_dev->device;
+ if (pci_device == 0x4301)
+ chip_id_16 = 0x4301;
+ else if ((pci_device >= 0x4305) && (pci_device <= 0x4307))
+ chip_id_16 = 0x4307;
+ else if ((pci_device >= 0x4402) && (pci_device <= 0x4403))
+ chip_id_16 = 0x4402;
+ else if ((pci_device >= 0x4610) && (pci_device <= 0x4615))
+ chip_id_16 = 0x4610;
+ else if ((pci_device >= 0x4710) && (pci_device <= 0x4715))
+ chip_id_16 = 0x4710;
+#ifdef CONFIG_BCM947XX
+ else if ((pci_device >= 0x4320) && (pci_device <= 0x4325))
+ chip_id_16 = 0x4309;
+#endif
+ else {
+ printk(KERN_ERR PFX "Could not determine Chip ID\n");
+ return -ENODEV;
+ }
+ }
+
+ /* ChipCommon with Core Rev >=4 encodes number of cores,
+ * otherwise consult hardcoded table */
+ if ((core_id == BCM43xx_COREID_CHIPCOMMON) && (core_rev >= 4)) {
+ core_count = (chip_id_32 & 0x0F000000) >> 24;
+ } else {
+ switch (chip_id_16) {
case 0x4610:
case 0x4704:
case 0x4710:
@@ -2865,6 +3238,7 @@
goto out;
}
bcm->nr_80211_available++;
+ core->priv = ext_80211;
bcm43xx_init_struct_phyinfo(&ext_80211->phy);
bcm43xx_init_struct_radioinfo(&ext_80211->radio);
break;
@@ -2876,174 +3250,21 @@
core->available = 1;
core->id = core_id;
core->rev = core_rev;
- core->index = current_core;
- }
- }
-
- if (!bcm->core_80211[0].available) {
- printk(KERN_ERR PFX "Error: No 80211 core found!\n");
- err = -ENODEV;
- goto out;
- }
-
- err = bcm43xx_switch_core(bcm, &bcm->core_80211[0]);
-
- assert(err == 0);
-out:
- return err;
-}
-
-static void bcm43xx_rate_memory_write(struct bcm43xx_private *bcm,
- u16 rate,
- int is_ofdm)
-{
- u16 offset;
-
- if (is_ofdm) {
- offset = 0x480;
- offset += (bcm43xx_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2;
- } else {
- offset = 0x4C0;
- offset += (bcm43xx_plcp_get_ratecode_cck(rate) & 0x000F) * 2;
- }
- bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, offset + 0x20,
- bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, offset));
-}
-
-static void bcm43xx_rate_memory_init(struct bcm43xx_private *bcm)
-{
- switch (bcm43xx_current_phy(bcm)->type) {
- case BCM43xx_PHYTYPE_A:
- case BCM43xx_PHYTYPE_G:
- bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_6MB, 1);
- bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_12MB, 1);
- bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_18MB, 1);
- bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_24MB, 1);
- bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_36MB, 1);
- bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_48MB, 1);
- bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_54MB, 1);
- case BCM43xx_PHYTYPE_B:
- bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_1MB, 0);
- bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_2MB, 0);
- bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_5MB, 0);
- bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_11MB, 0);
- break;
- default:
- assert(0);
- }
-}
-
-static void bcm43xx_wireless_core_cleanup(struct bcm43xx_private *bcm)
-{
- bcm43xx_chip_cleanup(bcm);
- bcm43xx_pio_free(bcm);
- bcm43xx_dma_free(bcm);
-
- bcm->current_core->initialized = 0;
-}
-
-/* http://bcm-specs.sipsolutions.net/80211Init */
-static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm)
-{
- struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
- struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
- u32 ucodeflags;
- int err;
- u32 sbimconfiglow;
- u8 limit;
-
- if (bcm->chip_rev < 5) {
- sbimconfiglow = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW);
- sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK;
- sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK;
- if (bcm->bustype == BCM43xx_BUSTYPE_PCI)
- sbimconfiglow |= 0x32;
- else if (bcm->bustype == BCM43xx_BUSTYPE_SB)
- sbimconfiglow |= 0x53;
- else
- assert(0);
- bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, sbimconfiglow);
- }
-
- bcm43xx_phy_calibrate(bcm);
- err = bcm43xx_chip_init(bcm);
- if (err)
- goto out;
-
- bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0016, bcm->current_core->rev);
- ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, BCM43xx_UCODEFLAGS_OFFSET);
-
- if (0 /*FIXME: which condition has to be used here? */)
- ucodeflags |= 0x00000010;
-
- /* HW decryption needs to be set now. */
- ucodeflags |= 0x40000000;
-
- if (phy->type == BCM43xx_PHYTYPE_G) {
- ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY;
- if (phy->rev == 1)
- ucodeflags |= BCM43xx_UCODEFLAG_UNKGPHY;
- if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)
- ucodeflags |= BCM43xx_UCODEFLAG_UNKPACTRL;
- } else if (phy->type == BCM43xx_PHYTYPE_B) {
- ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY;
- if (phy->rev >= 2 && radio->version == 0x2050)
- ucodeflags &= ~BCM43xx_UCODEFLAG_UNKGPHY;
- }
-
- if (ucodeflags != bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
- BCM43xx_UCODEFLAGS_OFFSET)) {
- bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
- BCM43xx_UCODEFLAGS_OFFSET, ucodeflags);
+ core->index = current_core;
+ }
}
- /* Short/Long Retry Limit.
- * The retry-limit is a 4-bit counter. Enforce this to avoid overflowing
- * the chip-internal counter.
- */
- limit = limit_value(modparam_short_retry, 0, 0xF);
- bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0006, limit);
- limit = limit_value(modparam_long_retry, 0, 0xF);
- bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0007, limit);
-
- bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0044, 3);
- bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0046, 2);
-
- bcm43xx_rate_memory_init(bcm);
-
- /* Minimum Contention Window */
- if (phy->type == BCM43xx_PHYTYPE_B)
- bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000001f);
- else
- bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000000f);
- /* Maximum Contention Window */
- bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff);
-
- bcm43xx_write_mac_bssid_templates(bcm);
-
- if (bcm->current_core->rev >= 5)
- bcm43xx_write16(bcm, 0x043C, 0x000C);
-
- if (bcm43xx_using_pio(bcm))
- err = bcm43xx_pio_init(bcm);
- else
- err = bcm43xx_dma_init(bcm);
- if (err)
- goto err_chip_cleanup;
- bcm43xx_write16(bcm, 0x0612, 0x0050);
- bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0416, 0x0050);
- bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0414, 0x01F4);
+ if (!bcm->core_80211[0].available) {
+ printk(KERN_ERR PFX "Error: No 80211 core found!\n");
+ err = -ENODEV;
+ goto out;
+ }
- bcm43xx_mac_enable(bcm);
- bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate);
+ err = bcm43xx_switch_core(bcm, &bcm->core_80211[0]);
- bcm->current_core->initialized = 1;
+ assert(err == 0);
out:
return err;
-
-err_chip_cleanup:
- bcm43xx_chip_cleanup(bcm);
- goto out;
}
static int bcm43xx_chipset_attach(struct bcm43xx_private *bcm)
@@ -3151,167 +3372,6 @@
return err;
}
-static void bcm43xx_periodic_every120sec(struct bcm43xx_private *bcm)
-{
- struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
-
- if (phy->type != BCM43xx_PHYTYPE_G || phy->rev < 2)
- return;
-
- bcm43xx_mac_suspend(bcm);
- bcm43xx_phy_lo_g_measure(bcm);
- bcm43xx_mac_enable(bcm);
-}
-
-static void bcm43xx_periodic_every60sec(struct bcm43xx_private *bcm)
-{
- bcm43xx_phy_lo_mark_all_unused(bcm);
- if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {
- bcm43xx_mac_suspend(bcm);
- bcm43xx_calc_nrssi_slope(bcm);
- bcm43xx_mac_enable(bcm);
- }
-}
-
-static void bcm43xx_periodic_every30sec(struct bcm43xx_private *bcm)
-{
- /* Update device statistics. */
- bcm43xx_calculate_link_quality(bcm);
-}
-
-static void bcm43xx_periodic_every15sec(struct bcm43xx_private *bcm)
-{
- struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
- struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
-
- if (phy->type == BCM43xx_PHYTYPE_G) {
- //TODO: update_aci_moving_average
- if (radio->aci_enable && radio->aci_wlan_automatic) {
- bcm43xx_mac_suspend(bcm);
- if (!radio->aci_enable && 1 /*TODO: not scanning? */) {
- if (0 /*TODO: bunch of conditions*/) {
- bcm43xx_radio_set_interference_mitigation(bcm,
- BCM43xx_RADIO_INTERFMODE_MANUALWLAN);
- }
- } else if (1/*TODO*/) {
- /*
- if ((aci_average > 1000) && !(bcm43xx_radio_aci_scan(bcm))) {
- bcm43xx_radio_set_interference_mitigation(bcm,
- BCM43xx_RADIO_INTERFMODE_NONE);
- }
- */
- }
- bcm43xx_mac_enable(bcm);
- } else if (radio->interfmode == BCM43xx_RADIO_INTERFMODE_NONWLAN &&
- phy->rev == 1) {
- //TODO: implement rev1 workaround
- }
- }
- bcm43xx_phy_xmitpower(bcm); //FIXME: unless scanning?
- //TODO for APHY (temperature?)
-}
-
-static void do_periodic_work(struct bcm43xx_private *bcm)
-{
- unsigned int state;
-
- state = bcm->periodic_state;
- if (state % 8 == 0)
- bcm43xx_periodic_every120sec(bcm);
- if (state % 4 == 0)
- bcm43xx_periodic_every60sec(bcm);
- if (state % 2 == 0)
- bcm43xx_periodic_every30sec(bcm);
- if (state % 1 == 0)
- bcm43xx_periodic_every15sec(bcm);
- bcm->periodic_state = state + 1;
-
- schedule_delayed_work(&bcm->periodic_work, HZ * 15);
-}
-
-/* Estimate a "Badness" value based on the periodic work
- * state-machine state. "Badness" is worse (bigger), if the
- * periodic work will take longer.
- */
-static int estimate_periodic_work_badness(unsigned int state)
-{
- int badness = 0;
-
- if (state % 8 == 0) /* every 120 sec */
- badness += 10;
- if (state % 4 == 0) /* every 60 sec */
- badness += 5;
- if (state % 2 == 0) /* every 30 sec */
- badness += 1;
- if (state % 1 == 0) /* every 15 sec */
- badness += 1;
-
-#define BADNESS_LIMIT 4
- return badness;
-}
-
-static void bcm43xx_periodic_work_handler(void *d)
-{
- struct bcm43xx_private *bcm = d;
- unsigned long flags;
- u32 savedirqs = 0;
- int badness;
-
- badness = estimate_periodic_work_badness(bcm->periodic_state);
- if (badness > BADNESS_LIMIT) {
- /* Periodic work will take a long time, so we want it to
- * be preemtible.
- */
- netif_stop_queue(bcm->net_dev);
- synchronize_net();
- spin_lock_irqsave(&bcm->irq_lock, flags);
- bcm43xx_mac_suspend(bcm);
- if (bcm43xx_using_pio(bcm))
- bcm43xx_pio_freeze_txqueues(bcm);
- savedirqs = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
- spin_unlock_irqrestore(&bcm->irq_lock, flags);
- mutex_lock(&bcm->mutex);
- bcm43xx_synchronize_irq(bcm);
- } else {
- /* Periodic work should take short time, so we want low
- * locking overhead.
- */
- mutex_lock(&bcm->mutex);
- spin_lock_irqsave(&bcm->irq_lock, flags);
- }
-
- do_periodic_work(bcm);
-
- if (badness > BADNESS_LIMIT) {
- spin_lock_irqsave(&bcm->irq_lock, flags);
- if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) {
- tasklet_enable(&bcm->isr_tasklet);
- bcm43xx_interrupt_enable(bcm, savedirqs);
- if (bcm43xx_using_pio(bcm))
- bcm43xx_pio_thaw_txqueues(bcm);
- bcm43xx_mac_enable(bcm);
- }
- netif_wake_queue(bcm->net_dev);
- }
- mmiowb();
- spin_unlock_irqrestore(&bcm->irq_lock, flags);
- mutex_unlock(&bcm->mutex);
-}
-
-static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm)
-{
- cancel_rearming_delayed_work(&bcm->periodic_work);
-}
-
-static void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm)
-{
- struct work_struct *work = &(bcm->periodic_work);
-
- assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
- INIT_WORK(work, bcm43xx_periodic_work_handler, bcm);
- schedule_work(work);
-}
-
static void bcm43xx_free_modes(struct bcm43xx_private *bcm)
{
struct ieee80211_hw *ieee = bcm->ieee;
@@ -3331,7 +3391,9 @@
int nr_channels,
const struct ieee80211_channel *channels,
int nr_rates,
- const struct ieee80211_rate *rates)
+ const struct ieee80211_rate *rates,
+ int nr_rates2,
+ const struct ieee80211_rate *rates2)
{
struct ieee80211_hw_modes *mode;
int err = -ENOMEM;
@@ -3340,609 +3402,87 @@
mode->mode = mode_id;
mode->num_channels = nr_channels;
- mode->channels = kzalloc(sizeof(*channels) * nr_channels, GFP_KERNEL);
- if (!mode->channels)
- goto out;
- memcpy(mode->channels, channels, sizeof(*channels) * nr_channels);
-
- mode->num_rates = nr_rates;
- mode->rates = kzalloc(sizeof(*rates) * nr_rates, GFP_KERNEL);
- if (!mode->rates)
- goto err_free_channels;
- memcpy(mode->rates, rates, sizeof(*rates) * nr_rates);
-
- ieee->num_modes++;
- err = 0;
-out:
- return err;
-
-err_free_channels:
- kfree(mode->channels);
- goto out;
-}
-
-static int bcm43xx_setup_modes_aphy(struct bcm43xx_private *bcm)
-{
- int err = 0;
-
- static const struct ieee80211_rate rates[] = {
- {
- .rate = 60,
- .val = BCM43xx_OFDM_RATE_6MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_6MB,
- }, {
- .rate = 90,
- .val = BCM43xx_OFDM_RATE_9MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_9MB,
- }, {
- .rate = 120,
- .val = BCM43xx_OFDM_RATE_12MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_12MB,
- }, {
- .rate = 180,
- .val = BCM43xx_OFDM_RATE_18MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_18MB,
- }, {
- .rate = 240,
- .val = BCM43xx_OFDM_RATE_24MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_24MB,
- }, {
- .rate = 360,
- .val = BCM43xx_OFDM_RATE_36MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_36MB,
- }, {
- .rate = 480,
- .val = BCM43xx_OFDM_RATE_48MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_48MB,
- }, {
- .rate = 540,
- .val = BCM43xx_OFDM_RATE_54MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_54MB,
- },
- };
- static const struct ieee80211_channel channels[] = {
- {
- .chan = 36,
- .freq = 5180,
- .val = 36,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 40,
- .freq = 5200,
- .val = 40,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 44,
- .freq = 5220,
- .val = 44,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 48,
- .freq = 5240,
- .val = 48,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 52,
- .freq = 5260,
- .val = 52,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 56,
- .freq = 5280,
- .val = 56,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 60,
- .freq = 5300,
- .val = 60,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 64,
- .freq = 5320,
- .val = 64,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 149,
- .freq = 5745,
- .val = 149,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 153,
- .freq = 5765,
- .val = 153,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 157,
- .freq = 5785,
- .val = 157,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 161,
- .freq = 5805,
- .val = 161,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 165,
- .freq = 5825,
- .val = 165,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- },
- };
-
- if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) {
- err = bcm43xx_append_mode(bcm->ieee, MODE_IEEE80211A,
- ARRAY_SIZE(channels), channels,
- ARRAY_SIZE(rates), rates);
- }
-
- return err;
-}
-
-static int bcm43xx_setup_modes_bphy(struct bcm43xx_private *bcm)
-{
- int err = 0;
-
- static const struct ieee80211_rate rates[] = {
- {
- .rate = 10,
- .val = BCM43xx_CCK_RATE_1MB,
- .flags = IEEE80211_RATE_CCK,
- .val2 = BCM43xx_CCK_RATE_1MB,
- }, {
- .rate = 20,
- .val = BCM43xx_CCK_RATE_2MB,
- .flags = IEEE80211_RATE_CCK_2,
- .val2 = BCM43xx_CCK_RATE_2MB,
- }, {
- .rate = 55,
- .val = BCM43xx_CCK_RATE_5MB,
- .flags = IEEE80211_RATE_CCK_2,
- .val2 = BCM43xx_CCK_RATE_5MB,
- }, {
- .rate = 110,
- .val = BCM43xx_CCK_RATE_11MB,
- .flags = IEEE80211_RATE_CCK_2,
- .val2 = BCM43xx_CCK_RATE_11MB,
- },
- };
- static const struct ieee80211_channel channels[] = {
- {
- .chan = 1,
- .freq = 2412,
- .val = 1,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 2,
- .freq = 2417,
- .val = 2,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 3,
- .freq = 2422,
- .val = 3,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 4,
- .freq = 2427,
- .val = 4,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 5,
- .freq = 2432,
- .val = 5,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 6,
- .freq = 2437,
- .val = 6,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 7,
- .freq = 2442,
- .val = 7,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 8,
- .freq = 2447,
- .val = 8,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 9,
- .freq = 2452,
- .val = 9,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 10,
- .freq = 2457,
- .val = 10,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 11,
- .freq = 2462,
- .val = 11,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 12,
- .freq = 2467,
- .val = 12,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 13,
- .freq = 2472,
- .val = 13,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, /*{
- .chan = 14,
- .freq = 2484,
- .val = 14,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- },*/
- };
-
- if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_B ||
- bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) {
- err = bcm43xx_append_mode(bcm->ieee, MODE_IEEE80211B,
- ARRAY_SIZE(channels), channels,
- ARRAY_SIZE(rates), rates);
- }
-
- return err;
-}
-
-static int bcm43xx_setup_modes_gphy(struct bcm43xx_private *bcm)
-{
- int err = 0;
-
- static const struct ieee80211_rate rates[] = {
- {
- .rate = 10,
- .val = BCM43xx_CCK_RATE_1MB,
- .flags = IEEE80211_RATE_CCK,
- .val2 = BCM43xx_CCK_RATE_1MB,
- }, {
- .rate = 20,
- .val = BCM43xx_CCK_RATE_2MB,
- .flags = IEEE80211_RATE_CCK_2,
- .val2 = BCM43xx_CCK_RATE_2MB,
- }, {
- .rate = 55,
- .val = BCM43xx_CCK_RATE_5MB,
- .flags = IEEE80211_RATE_CCK_2,
- .val2 = BCM43xx_CCK_RATE_5MB,
- }, {
- .rate = 60,
- .val = BCM43xx_OFDM_RATE_6MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_6MB,
- }, {
- .rate = 90,
- .val = BCM43xx_OFDM_RATE_9MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_9MB,
- }, {
- .rate = 110,
- .val = BCM43xx_CCK_RATE_11MB,
- .flags = IEEE80211_RATE_CCK_2,
- .val2 = BCM43xx_CCK_RATE_11MB,
- }, {
- .rate = 120,
- .val = BCM43xx_OFDM_RATE_12MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_12MB,
- }, {
- .rate = 180,
- .val = BCM43xx_OFDM_RATE_18MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_18MB,
- }, {
- .rate = 240,
- .val = BCM43xx_OFDM_RATE_24MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_24MB,
- }, {
- .rate = 360,
- .val = BCM43xx_OFDM_RATE_36MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_36MB,
- }, {
- .rate = 480,
- .val = BCM43xx_OFDM_RATE_48MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_48MB,
- }, {
- .rate = 540,
- .val = BCM43xx_OFDM_RATE_54MB,
- .flags = IEEE80211_RATE_OFDM,
- .val2 = BCM43xx_OFDM_RATE_54MB,
- },
- };
- static const struct ieee80211_channel channels[] = {
- {
- .chan = 1,
- .freq = 2412,
- .val = 1,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 2,
- .freq = 2417,
- .val = 2,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 3,
- .freq = 2422,
- .val = 3,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 4,
- .freq = 2427,
- .val = 4,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 5,
- .freq = 2432,
- .val = 5,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 6,
- .freq = 2437,
- .val = 6,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 7,
- .freq = 2442,
- .val = 7,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 8,
- .freq = 2447,
- .val = 8,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 9,
- .freq = 2452,
- .val = 9,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 10,
- .freq = 2457,
- .val = 10,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 11,
- .freq = 2462,
- .val = 11,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 12,
- .freq = 2467,
- .val = 12,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, {
- .chan = 13,
- .freq = 2472,
- .val = 13,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- }, /*{
- .chan = 14,
- .freq = 2484,
- .val = 14,
- .flag = IEEE80211_CHAN_W_SCAN |
- IEEE80211_CHAN_W_ACTIVE_SCAN |
- IEEE80211_CHAN_W_IBSS,
- .power_level = 0xFF,
- .antenna_max = 0xFF,
- },*/
- };
+ mode->channels = kzalloc(sizeof(*channels) * nr_channels, GFP_KERNEL);
+ if (!mode->channels)
+ goto out;
+ memcpy(mode->channels, channels, sizeof(*channels) * nr_channels);
- if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) {
- err = bcm43xx_append_mode(bcm->ieee, MODE_IEEE80211G,
- ARRAY_SIZE(channels), channels,
- ARRAY_SIZE(rates), rates);
+ mode->num_rates = nr_rates;
+ mode->rates = kzalloc(sizeof(*rates) * (nr_rates + nr_rates2),
+ GFP_KERNEL);
+ if (!mode->rates)
+ goto err_free_channels;
+ memcpy(mode->rates, rates, sizeof(*rates) * nr_rates);
+ if (nr_rates2) {
+ mode->num_rates += nr_rates2;
+ memcpy(mode->rates + nr_rates, rates2,
+ sizeof(*rates2) * nr_rates2);
}
+ ieee->num_modes++;
+ err = 0;
+out:
return err;
+
+err_free_channels:
+ kfree(mode->channels);
+ goto out;
}
static int bcm43xx_setup_modes(struct bcm43xx_private *bcm)
{
- struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
int err = -ENOMEM;
- int nr;
struct ieee80211_hw *ieee = bcm->ieee;
-
- if (phy->type == BCM43xx_PHYTYPE_A)
- nr = 1;
- else if (phy->type == BCM43xx_PHYTYPE_B)
- nr = 1;
- else
- nr = 2;
- ieee->modes = kzalloc(sizeof(*(ieee->modes)) * nr, GFP_KERNEL);
+ struct bcm43xx_coreinfo *core;
+ struct bcm43xx_coreinfo_80211 *wlext;
+ int i, nr_modes;
+
+ nr_modes = bcm->nr_80211_available;
+ ieee->modes = kzalloc(sizeof(*(ieee->modes)) * nr_modes,
+ GFP_KERNEL);
if (!ieee->modes)
goto out;
ieee->num_modes = 0;
- err = bcm43xx_setup_modes_aphy(bcm);
- if (err)
- goto error;
- err = bcm43xx_setup_modes_gphy(bcm);
- if (err)
- goto error;
- err = bcm43xx_setup_modes_bphy(bcm);
- if (err)
- goto error;
+ for (i = 0; i < bcm->nr_80211_available; i++) {
+ core = &(bcm->core_80211[i]);
+ wlext = core->priv;
- assert(ieee->num_modes == nr && nr > 0);
+ switch (wlext->phy.type) {
+ case BCM43xx_PHYTYPE_A:
+ err = bcm43xx_append_mode(bcm->ieee, MODE_IEEE80211A,
+ ARRAY_SIZE(bcm43xx_a_chantable),
+ bcm43xx_a_chantable,
+ ARRAY_SIZE(bcm43xx_ofdm_ratetable),
+ bcm43xx_ofdm_ratetable,
+ 0, NULL);
+ break;
+ case BCM43xx_PHYTYPE_B:
+ err = bcm43xx_append_mode(bcm->ieee, MODE_IEEE80211B,
+ ARRAY_SIZE(bcm43xx_bg_chantable),
+ bcm43xx_bg_chantable,
+ ARRAY_SIZE(bcm43xx_cck_ratetable),
+ bcm43xx_cck_ratetable,
+ 0, NULL);
+ break;
+ case BCM43xx_PHYTYPE_G:
+ err = bcm43xx_append_mode(bcm->ieee, MODE_IEEE80211G,
+ ARRAY_SIZE(bcm43xx_bg_chantable),
+ bcm43xx_bg_chantable,
+ ARRAY_SIZE(bcm43xx_ofdm_ratetable),
+ bcm43xx_ofdm_ratetable,
+ ARRAY_SIZE(bcm43xx_cck_ratetable),
+ bcm43xx_cck_ratetable);
+ break;
+ default:
+ err = 0;
+ assert(0);
+ }
+ if (err)
+ goto error;
+ }
out:
return err;
-
error:
bcm43xx_free_modes(bcm);
goto out;
@@ -3955,99 +3495,222 @@
bcm43xx_clear_keys(bcm);
}
-/* This is the opposite of bcm43xx_init_board() */
-static void bcm43xx_free_board(struct bcm43xx_private *bcm)
+static void prepare_phydata_for_init(struct bcm43xx_phyinfo *phy)
{
- int i, err;
+ phy->antenna_diversity = 0xFFFF;
+ memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig));
+ memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos));
- mutex_lock(&bcm->mutex);
- bcm43xx_sysfs_unregister(bcm);
- bcm43xx_periodic_tasks_delete(bcm);
+ /* Flags */
+ phy->calibrated = 0;
+ phy->is_locked = 0;
- bcm43xx_set_status(bcm, BCM43xx_STAT_SHUTTINGDOWN);
+ if (phy->_lo_pairs) {
+ memset(phy->_lo_pairs, 0,
+ sizeof(struct bcm43xx_lopair) * BCM43xx_LO_COUNT);
+ }
+ memset(phy->loopback_gain, 0, sizeof(phy->loopback_gain));
+}
+
+static void prepare_radiodata_for_init(struct bcm43xx_private *bcm,
+ struct bcm43xx_radioinfo *radio)
+{
+ int i;
+
+ /* Set default attenuation values. */
+ radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm);
+ radio->radio_atten = bcm43xx_default_radio_attenuation(bcm);
+ radio->txctl1 = bcm43xx_default_txctl1(bcm);
+ radio->txctl2 = 0xFFFF;
+ radio->txpwr_offset = 0;
+
+ /* NRSSI */
+ radio->nrssislope = 0;
+ for (i = 0; i < ARRAY_SIZE(radio->nrssi); i++)
+ radio->nrssi[i] = -1000;
+ for (i = 0; i < ARRAY_SIZE(radio->nrssi_lt); i++)
+ radio->nrssi_lt[i] = i;
+
+ radio->lofcal = 0xFFFF;
+ radio->initval = 0xFFFF;
+
+ radio->aci_enable = 0;
+ radio->aci_wlan_automatic = 0;
+ radio->aci_hw_rssi = 0;
+}
+
+static void prepare_priv_for_init(struct bcm43xx_private *bcm)
+{
+ int i;
+ struct bcm43xx_coreinfo *core;
+ struct bcm43xx_coreinfo_80211 *wlext;
+
+ assert(!bcm->active_80211_core);
+
+ bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING);
+ /* Flags */
+ bcm->was_initialized = 0;
+ bcm->reg124_set_0x4 = 0;
+
+ /* Stats */
+ memset(&bcm->stats, 0, sizeof(bcm->stats));
+
+ /* Wireless core data */
for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {
- if (!bcm->core_80211[i].available)
- continue;
- if (!bcm->core_80211[i].initialized)
+ core = &(bcm->core_80211[i]);
+ wlext = core->priv;
+
+ if (!core->available)
continue;
+ assert(wlext == &(bcm->core_80211_ext[i]));
- err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]);
- assert(err == 0);
- bcm43xx_wireless_core_cleanup(bcm);
+ prepare_phydata_for_init(&wlext->phy);
+ prepare_radiodata_for_init(bcm, &wlext->radio);
}
- bcm43xx_pctl_set_crystal(bcm, 0);
- bcm43xx_free_modes(bcm);
+ /* IRQ related flags */
+ bcm->irq_reason = 0;
+ memset(bcm->dma_reason, 0, sizeof(bcm->dma_reason));
+ bcm->irq_savedstate = BCM43xx_IRQ_INITIAL;
- bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT);
- mutex_unlock(&bcm->mutex);
+ bcm->mac_suspended = 1;
+
+ /* Noise calculation context */
+ memset(&bcm->noisecalc, 0, sizeof(bcm->noisecalc));
+
+ /* Periodic work context */
+ bcm->periodic_state = 0;
}
-static int bcm43xx_init_board(struct bcm43xx_private *bcm)
+static int wireless_core_up(struct bcm43xx_private *bcm,
+ int active_wlcore)
+{
+ int err;
+
+ if (!bcm43xx_core_enabled(bcm))
+ bcm43xx_wireless_core_reset(bcm, 1);
+ if (!active_wlcore)
+ bcm43xx_wireless_core_mark_inactive(bcm);
+ err = bcm43xx_wireless_core_init(bcm, active_wlcore);
+ if (err)
+ goto out;
+ if (!active_wlcore)
+ bcm43xx_radio_turn_off(bcm);
+out:
+ return err;
+}
+
+/* Select and enable the "to be used" wireless core.
+ * Locking: bcm->mutex must be aquired before calling this.
+ * bcm->irq_lock must not be aquired.
+ */
+int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm,
+ int phytype)
{
int i, err;
- int connect_phy;
+ struct bcm43xx_coreinfo *active_core = NULL;
+ struct bcm43xx_coreinfo_80211 *active_wlext = NULL;
+ struct bcm43xx_coreinfo *core;
+ struct bcm43xx_coreinfo_80211 *wlext;
+ int adjust_active_sbtmstatelow = 0;
might_sleep();
- mutex_lock(&bcm->mutex);
- bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING);
+ if (phytype < 0) {
+ /* If no phytype is requested, select the first core. */
+ assert(bcm->core_80211[0].available);
+ wlext = bcm->core_80211[0].priv;
+ phytype = wlext->phy.type;
+ }
+ /* Find the requested core. */
+ for (i = 0; i < bcm->nr_80211_available; i++) {
+ core = &(bcm->core_80211[i]);
+ wlext = core->priv;
+ if (wlext->phy.type == phytype) {
+ active_core = core;
+ active_wlext = wlext;
+ break;
+ }
+ }
+ if (!active_core)
+ return -ESRCH; /* No such PHYTYPE on this board. */
+
+ if (bcm->active_80211_core) {
+ /* We already selected a wl core in the past.
+ * So first clean up everything.
+ */
+ dprintk(KERN_INFO PFX "select_wireless_core: cleanup\n");
+ ieee80211_netif_oper(bcm->net_dev, NETIF_STOP);
+ ieee80211_netif_oper(bcm->net_dev, NETIF_DETACH);
+ bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED);
+ err = bcm43xx_disable_interrupts_sync(bcm);
+ assert(!err);
+ tasklet_enable(&bcm->isr_tasklet);
+ err = bcm43xx_shutdown_all_wireless_cores(bcm);
+ if (err)
+ goto error;
+ /* Ok, everything down, continue to re-initialize. */
+ bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING);
+ }
+
+ /* Reset all data structures. */
+ prepare_priv_for_init(bcm);
- err = bcm43xx_pctl_set_crystal(bcm, 1);
- if (err)
- goto out;
- err = bcm43xx_pctl_init(bcm);
- if (err)
- goto err_crystal_off;
err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_FAST);
if (err)
- goto err_crystal_off;
+ goto error;
- tasklet_enable(&bcm->isr_tasklet);
+ /* Mark all unused cores "inactive". */
for (i = 0; i < bcm->nr_80211_available; i++) {
- err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]);
- assert(err != -ENODEV);
- if (err)
- goto err_80211_unwind;
+ core = &(bcm->core_80211[i]);
+ wlext = core->priv;
- /* Enable the selected wireless core.
- * Connect PHY only on the first core.
- */
- if (!bcm43xx_core_enabled(bcm)) {
- if (bcm->nr_80211_available == 1) {
- connect_phy = bcm43xx_current_phy(bcm)->connected;
- } else {
- if (i == 0)
- connect_phy = 1;
- else
- connect_phy = 0;
- }
- bcm43xx_wireless_core_reset(bcm, connect_phy);
+ if (core == active_core)
+ continue;
+ err = bcm43xx_switch_core(bcm, core);
+ if (err) {
+ dprintk(KERN_ERR PFX "Could not switch to inactive "
+ "802.11 core (%d)\n", err);
+ goto error;
}
+ err = wireless_core_up(bcm, 0);
+ if (err) {
+ dprintk(KERN_ERR PFX "core_up for inactive 802.11 core "
+ "failed (%d)\n", err);
+ goto error;
+ }
+ adjust_active_sbtmstatelow = 1;
+ }
- if (i != 0)
- bcm43xx_wireless_core_mark_inactive(bcm, &bcm->core_80211[0]);
-
- err = bcm43xx_wireless_core_init(bcm);
- if (err)
- goto err_80211_unwind;
+ /* Now initialize the active 802.11 core. */
+ err = bcm43xx_switch_core(bcm, active_core);
+ if (err) {
+ dprintk(KERN_ERR PFX "Could not switch to active "
+ "802.11 core (%d)\n", err);
+ goto error;
+ }
+ if (adjust_active_sbtmstatelow &&
+ active_wlext->phy.type == BCM43xx_PHYTYPE_G) {
+ u32 sbtmstatelow;
- if (i != 0) {
- bcm43xx_mac_suspend(bcm);
- bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
- bcm43xx_radio_turn_off(bcm);
- }
+ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
+ sbtmstatelow |= 0x20000000;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
}
- bcm->active_80211_core = &bcm->core_80211[0];
- if (bcm->nr_80211_available >= 2) {
- bcm43xx_switch_core(bcm, &bcm->core_80211[0]);
- bcm43xx_mac_enable(bcm);
+ err = wireless_core_up(bcm, 1);
+ if (err) {
+ dprintk(KERN_ERR PFX "core_up for active 802.11 core "
+ "failed (%d)\n", err);
+ goto error;
}
+ err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC);
+ if (err)
+ goto error;
+ bcm->active_80211_core = active_core;
+
bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC);
bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_SELF, (u8 *)(bcm->net_dev->dev_addr));
- dprintk(KERN_INFO PFX "80211 cores initialized\n");
- bcm43xx_setup_modes(bcm);
bcm43xx_security_init(bcm);
bcm43xx_measure_channel_change_time(bcm);
ieee80211_update_hw(bcm->net_dev, bcm->ieee);
@@ -4055,37 +3718,59 @@
ieee80211_netif_oper(bcm->net_dev, NETIF_START);
ieee80211_netif_oper(bcm->net_dev, NETIF_WAKE);
- bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC);
+ /* Let's go! Be careful after enabling the IRQs.
+ * Don't switch cores, for example.
+ */
+ bcm43xx_mac_enable(bcm);
+ bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED);
+ err = bcm43xx_initialize_irq(bcm);
+ if (err)
+ goto error;
+ bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate);
+
+ dprintk(KERN_INFO PFX "Selected 802.11 core (phytype %d)\n",
+ active_wlext->phy.type);
- if (bcm43xx_current_radio(bcm)->initial_channel != 0xFF) {
- bcm43xx_mac_suspend(bcm);
- bcm43xx_radio_selectchannel(bcm, bcm43xx_current_radio(bcm)->initial_channel, 0);
- bcm43xx_mac_enable(bcm);
- }
+ return 0;
- /* Initialization of the board is done. Flag it as such. */
- bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED);
+error:
+ bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT);
+ bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW);
+ return err;
+}
- bcm43xx_periodic_tasks_setup(bcm);
- bcm43xx_sysfs_register(bcm);
+static int bcm43xx_init_board(struct bcm43xx_private *bcm)
+{
+ int err;
- assert(err == 0);
+ mutex_lock(&bcm->mutex);
+
+ tasklet_enable(&bcm->isr_tasklet);
+ err = bcm43xx_pctl_set_crystal(bcm, 1);
+ if (err)
+ goto err_tasklet;
+ err = bcm43xx_pctl_init(bcm);
+ if (err)
+ goto err_crystal_off;
+ err = bcm43xx_select_wireless_core(bcm, -1);
+ if (err)
+ goto err_crystal_off;
+
+ bcm43xx_periodic_tasks_setup(bcm);
+ err = bcm43xx_sysfs_register(bcm);
+ if (err)
+ goto err_wlshutdown;
out:
mutex_unlock(&bcm->mutex);
return err;
-err_80211_unwind:
- tasklet_disable(&bcm->isr_tasklet);
- /* unwind all 80211 initialization */
- for (i = 0; i < bcm->nr_80211_available; i++) {
- if (!bcm->core_80211[i].initialized)
- continue;
- bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
- bcm43xx_wireless_core_cleanup(bcm);
- }
+err_wlshutdown:
+ bcm43xx_shutdown_all_wireless_cores(bcm);
err_crystal_off:
bcm43xx_pctl_set_crystal(bcm, 0);
+err_tasklet:
+ tasklet_disable(&bcm->isr_tasklet);
goto out;
}
@@ -4106,6 +3791,7 @@
if (bcm->core_80211_ext[i].phy.dyn_tssi_tbl)
kfree(bcm->core_80211_ext[i].phy.tssi2dbm);
}
+ bcm43xx_free_modes(bcm);
}
static int bcm43xx_read_phyinfo(struct bcm43xx_private *bcm)
@@ -4263,6 +3949,7 @@
memcpy(bcm->net_dev->dev_addr, bcm->sprom.et1macaddr, 6);
else
memcpy(bcm->net_dev->dev_addr, bcm->sprom.il0macaddr, 6);
+ bcm43xx_setup_modes(bcm);
snprintf(bcm->nick, IW_ESSID_MAX_SIZE,
"Broadcom %04X", bcm->chip_id);
@@ -4328,16 +4015,44 @@
struct bcm43xx_radioinfo *radio;
struct bcm43xx_phyinfo *phy;
unsigned long flags;
+ int new_phymode = -1;
+ int new_antennadiv = 0;
+ int err = 0;
+ mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)
goto out_unlock;
- radio = bcm43xx_current_radio(bcm);
phy = bcm43xx_current_phy(bcm);
+ radio = bcm43xx_current_radio(bcm);
+
+ /* Switch the PHY mode (if necessary). */
+ switch (conf->phymode) {
+ case MODE_IEEE80211A:
+ new_phymode = BCM43xx_PHYTYPE_A;
+ break;
+ case MODE_IEEE80211B:
+ new_phymode = BCM43xx_PHYTYPE_B;
+ break;
+ case MODE_IEEE80211G:
+ new_phymode = BCM43xx_PHYTYPE_G;
+ break;
+ default:
+ assert(0);
+ }
+ if (phy->type != new_phymode) {
+ err = bcm43xx_select_wireless_core(bcm, new_phymode);
+ if (err)
+ goto out_unlock;
+ phy = bcm43xx_current_phy(bcm);
+ radio = bcm43xx_current_radio(bcm);
+ }
- if (conf->channel != radio->channel)
- bcm43xx_radio_selectchannel(bcm, conf->channel, 0);
+ /* Switch to the requested channel. */
+ if (conf->channel_val != radio->channel)
+ bcm43xx_radio_selectchannel(bcm, conf->channel_val, 0);
+ /* Enable/Disable ShortSlot timing. */
if (conf->short_slot_time != bcm->short_slot) {
assert(phy->type == BCM43xx_PHYTYPE_G);
if (conf->short_slot_time)
@@ -4346,11 +4061,13 @@
bcm43xx_short_slot_timing_disable(bcm);
}
+ /* Adjust the desired TX power level. */
if (conf->power_level != 0) {
radio->power_level = conf->power_level;
bcm43xx_phy_xmitpower(bcm);
}
+ /* Hide/Show the SSID (AP mode only). */
if (conf->ssid_hidden) {
bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)
@@ -4361,20 +4078,26 @@
& ~BCM43xx_SBF_NO_SSID_BCAST);
}
-//FIXME: This does not seem to wake up:
-#if 0
- if (conf->power_level == 0) {
- if (radio->enabled)
- bcm43xx_radio_turn_off(bcm);
- } else {
- if (!radio->enabled)
- bcm43xx_radio_turn_on(bcm);
+ /* Select the TX Antenna. */
+ switch (conf->antenna_sel) {
+ case 0: /* default/diversity */
+ new_antennadiv = BCM43xx_RADIO_TXANTENNA_DEFAULT;
+ break;
+ case 1: /* Antenna 0 */
+ new_antennadiv = BCM43xx_RADIO_TXANTENNA_0;
+ break;
+ case 2: /* Antenna 1 */
+ new_antennadiv = BCM43xx_RADIO_TXANTENNA_1;
+ break;
+ default:
+ assert(0);
+ }
+ if (new_antennadiv != phy->antenna_diversity) {
+ phy->antenna_diversity = new_antennadiv;
+ bcm43xx_phy_set_antenna_diversity(bcm);
}
-#endif
-
- //TODO: phymode
- //TODO: antennas
+ /* Update templates for AP mode. */
if (bcm43xx_is_mode(bcm, IEEE80211_IF_TYPE_AP)) {
bcm43xx_set_beacon_int(bcm, conf->beacon_int);
bcm43xx_refresh_templates(bcm);
@@ -4382,8 +4105,9 @@
out_unlock:
spin_unlock_irqrestore(&bcm->irq_lock, flags);
+ mutex_unlock(&bcm->mutex);
- return 0;
+ return err;
}
static int bcm43xx_net_set_key(struct net_device *net_dev,
@@ -4504,7 +4228,8 @@
unsigned long flags;
local_irq_save(flags);
- bcm43xx_interrupt_handler(bcm->irq, bcm, NULL);
+ if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)
+ bcm43xx_interrupt_handler(bcm->irq, bcm, NULL);
local_irq_restore(flags);
}
#endif /* CONFIG_NET_POLL_CONTROLLER */
@@ -4522,7 +4247,7 @@
int err;
if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
- err = bcm43xx_disable_interrupts_sync(bcm, NULL);
+ err = bcm43xx_disable_interrupts_sync(bcm);
assert(!err);
bcm43xx_free_board(bcm);
}
@@ -4781,7 +4506,6 @@
if (bcm->cached_beacon)
kfree_skb(bcm->cached_beacon);
bcm->cached_beacon = NULL;
- assert(bcm->ucode == NULL);
ieee80211_free_hw(net_dev);
kfree(ieee);
}
@@ -4792,48 +4516,26 @@
static void bcm43xx_chip_reset(void *_bcm)
{
struct bcm43xx_private *bcm = _bcm;
- struct net_device *net_dev = bcm->net_dev;
- struct pci_dev *pci_dev = bcm->pci_dev;
- struct ieee80211_hw *ieee = bcm->ieee;
+ struct bcm43xx_phyinfo *phy;
int err;
- int was_initialized = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
-
- ieee80211_netif_oper(bcm->net_dev, NETIF_DETACH);
- tasklet_disable(&bcm->isr_tasklet);
- bcm->firmware_norelease = 1;
- if (was_initialized)
- bcm43xx_free_board(bcm);
- bcm->firmware_norelease = 0;
- bcm43xx_detach_board(bcm);
- err = bcm43xx_init_private(bcm, net_dev, pci_dev, ieee);
- if (err)
- goto failure;
- err = bcm43xx_attach_board(bcm);
- if (err)
- goto failure;
- if (was_initialized) {
- err = bcm43xx_init_board(bcm);
- if (err)
- goto failure;
- }
- ieee80211_netif_oper(bcm->net_dev, NETIF_ATTACH);
- printk(KERN_INFO PFX "Controller restarted\n");
+ mutex_lock(&bcm->mutex);
+ phy = bcm43xx_current_phy(bcm);
+ err = bcm43xx_select_wireless_core(bcm, phy->type);
+ mutex_unlock(&bcm->mutex);
- return;
-failure:
- printk(KERN_ERR PFX "Controller restart failed\n");
+ printk("%s" PFX "Controller restart%s\n",
+ (err == 0) ? KERN_INFO : KERN_ERR,
+ (err == 0) ? "ed" : " failed");
}
/* Hard-reset the chip.
* This can be called from interrupt or process context.
- * Make sure to _not_ re-enable device interrupts after this has been called.
*/
void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason)
{
+ assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
bcm43xx_set_status(bcm, BCM43xx_STAT_RESTARTING);
- bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
- bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */
printk(KERN_ERR PFX "Controller RESET (%s) ...\n", reason);
INIT_WORK(&bcm->restart_work, bcm43xx_chip_reset, bcm);
schedule_work(&bcm->restart_work);
@@ -4845,17 +4547,16 @@
{
struct net_device *net_dev = pci_get_drvdata(pdev);
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
- int try_to_shutdown = 0, err;
+ int err;
dprintk(KERN_INFO PFX "Suspending...\n");
- bcm->was_initialized = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
- if (bcm->was_initialized)
- try_to_shutdown = 1;
-
+ ieee80211_netif_oper(bcm->net_dev, NETIF_STOP);
ieee80211_netif_oper(bcm->net_dev, NETIF_DETACH);
- if (try_to_shutdown) {
- err = bcm43xx_disable_interrupts_sync(bcm, &bcm->irq_savedstate);
+ bcm->was_initialized = 0;
+ if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
+ bcm->was_initialized = 1;
+ err = bcm43xx_disable_interrupts_sync(bcm);
if (unlikely(err)) {
dprintk(KERN_ERR PFX "Suspend failed.\n");
return -EAGAIN;
@@ -4888,16 +4589,14 @@
pci_restore_state(pdev);
bcm43xx_chipset_attach(bcm);
- if (bcm->was_initialized) {
- bcm->irq_savedstate = BCM43xx_IRQ_INITIAL;
+ if (bcm->was_initialized)
err = bcm43xx_init_board(bcm);
- }
if (err) {
printk(KERN_ERR PFX "Resume failed!\n");
return err;
}
-
ieee80211_netif_oper(bcm->net_dev, NETIF_ATTACH);
+ ieee80211_netif_oper(bcm->net_dev, NETIF_WAKE);
dprintk(KERN_INFO PFX "Device resumed.\n");
Index: wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_debugfs.c
===================================================================
--- wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_debugfs.c 2006-07-10 00:41:15.000000000 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_debugfs.c 2006-07-10 00:54:11.000000000 +0200
@@ -316,6 +316,42 @@
return res;
}
+static ssize_t restart_write_file(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct bcm43xx_private *bcm = file->private_data;
+ char *buf = really_big_buffer;
+ ssize_t buf_size;
+ ssize_t res;
+ unsigned long flags;
+
+ buf_size = min(count, sizeof (really_big_buffer) - 1);
+ down(&big_buffer_sem);
+ if (copy_from_user(buf, user_buf, buf_size)) {
+ res = -EFAULT;
+ goto out_up;
+ }
+ mutex_lock(&bcm->mutex);
+ spin_lock_irqsave(&bcm->irq_lock, flags);
+ if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) {
+ printk(KERN_INFO PFX "debugfs: Board not initialized.\n");
+ res = -EFAULT;
+ goto out_unlock;
+ }
+ if (count > 0 && buf[0] == '1') {
+ bcm43xx_controller_restart(bcm, "manually restarted");
+ res = count;
+ } else
+ res = -EINVAL;
+
+out_unlock:
+ spin_unlock_irqrestore(&bcm->irq_lock, flags);
+ mutex_unlock(&bcm->mutex);
+out_up:
+ up(&big_buffer_sem);
+ return res;
+}
+
#undef fappend
@@ -349,6 +385,11 @@
.open = open_file_generic,
};
+static struct file_operations restart_fops = {
+ .write = restart_write_file,
+ .open = open_file_generic,
+};
+
void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm)
{
@@ -400,6 +441,10 @@
bcm, &txstat_fops);
if (!e->dentry_txstat)
printk(KERN_ERR PFX "debugfs: creating \"tx_status\" for \"%s\" failed!\n", devdir);
+ e->dentry_restart = debugfs_create_file("restart", 0222, e->subdir,
+ bcm, &restart_fops);
+ if (!e->dentry_restart)
+ printk(KERN_ERR PFX "debugfs: creating \"restart\" for \"%s\" failed!\n", devdir);
}
void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm)
@@ -415,6 +460,7 @@
debugfs_remove(e->dentry_devinfo);
debugfs_remove(e->dentry_tsf);
debugfs_remove(e->dentry_txstat);
+ debugfs_remove(e->dentry_restart);
debugfs_remove(e->subdir);
kfree(e->xmitstatus_buffer);
kfree(e->xmitstatus_print_buffer);
Index: wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_debugfs.h
===================================================================
--- wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_debugfs.h 2006-07-10 00:41:15.000000000 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_debugfs.h 2006-07-10 00:54:11.000000000 +0200
@@ -20,6 +20,7 @@
struct dentry *dentry_spromdump;
struct dentry *dentry_tsf;
struct dentry *dentry_txstat;
+ struct dentry *dentry_restart;
struct bcm43xx_private *bcm;
Index: wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.h
===================================================================
--- wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.h 2006-07-10 00:41:15.000000000 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.h 2006-07-10 00:54:11.000000000 +0200
@@ -152,6 +152,9 @@
int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core);
+int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm,
+ int phytype);
+
void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy);
void bcm43xx_mac_suspend(struct bcm43xx_private *bcm);
--
Greetings Michael.
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2006-07-10 21:40 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-07-10 21:38 [PATCH] bcm43xx-d80211: init routine rewrite Michael Buesch
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).