Commit e4d6b795 authored by Michael Buesch's avatar Michael Buesch Committed by David S. Miller

[B43]: add mac80211-based driver for modern BCM43xx devices

Signed-off-by: default avatarMichael Buesch <mb@bu3sch.de>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 61e115a5
......@@ -795,6 +795,15 @@ L: linux-hams@vger.kernel.org
W: http://www.baycom.org/~tom/ham/ham.html
S: Maintained
B43 WIRELESS DRIVER
P: Michael Buesch
M: mb@bu3sch.de
P: Stefano Brivio
M: st3@riseup.net
L: linux-wireless@vger.kernel.org
W: http://bcm43xx.berlios.de/
S: Maintained
BCM43XX WIRELESS DRIVER (SOFTMAC BASED VERSION)
P: Larry Finger
M: Larry.Finger@lwfinger.net
......
......@@ -579,6 +579,7 @@ config ADM8211
source "drivers/net/wireless/hostap/Kconfig"
source "drivers/net/wireless/bcm43xx/Kconfig"
source "drivers/net/wireless/b43/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
endmenu
......@@ -36,6 +36,7 @@ obj-$(CONFIG_PRISM54) += prism54/
obj-$(CONFIG_HOSTAP) += hostap/
obj-$(CONFIG_BCM43XX) += bcm43xx/
obj-$(CONFIG_B43) += b43/
obj-$(CONFIG_ZD1211RW) += zd1211rw/
# 16-bit wireless PCMCIA client drivers
......
config B43
tristate "Broadcom 43xx wireless support (mac80211 stack)"
depends on SSB_POSSIBLE && MAC80211 && WLAN_80211
select SSB
select FW_LOADER
select HW_RANDOM
---help---
b43 is a driver for the Broadcom 43xx series wireless devices.
Check "lspci" for something like
"Broadcom Corporation BCM43XX 802.11 Wireless LAN Controller"
to determine whether you own such a device.
This driver supports the new BCM43xx IEEE 802.11G devices, but not
the old IEEE 802.11B devices. Old devices are supported by
the b43legacy driver.
Note that this has nothing to do with the standard that your AccessPoint
supports (A, B, G or a combination).
IEEE 802.11G devices can talk to IEEE 802.11B AccessPoints.
It is safe to include both b43 and b43legacy as the underlying glue
layer will automatically load the correct version for your device.
This driver uses V4 firmware, which must be installed separately using
b43-fwcutter.
This driver can be built as a module (recommended) that will be called "b43".
If unsure, say M.
# Auto-select SSB PCI-HOST support, if possible
config B43_PCI_AUTOSELECT
bool
depends on B43 && SSB_PCIHOST_POSSIBLE
select SSB_PCIHOST
default y
# Auto-select SSB PCICORE driver, if possible
config B43_PCICORE_AUTOSELECT
bool
depends on B43 && SSB_DRIVER_PCICORE_POSSIBLE
select SSB_DRIVER_PCICORE
default y
config B43_PCMCIA
bool "Broadcom 43xx PCMCIA device support (EXPERIMENTAL)"
depends on B43 && SSB_PCMCIAHOST_POSSIBLE && EXPERIMENTAL
select SSB_PCMCIAHOST
---help---
Broadcom 43xx PCMCIA device support.
Support for 16bit PCMCIA devices.
Please note that most PC-CARD devices are _NOT_ 16bit PCMCIA
devices, but 32bit CardBUS devices. CardBUS devices are supported
out of the box by b43.
With this config option you can drive b43 cards in
CompactFlash formfactor in a PCMCIA adaptor.
CF b43 cards can sometimes be found in handheld PCs.
It's safe to select Y here, even if you don't have a B43 PCMCIA device.
If unsure, say N.
config B43_DEBUG
bool "Broadcom 43xx debugging"
depends on B43
---help---
Broadcom 43xx debugging messages.
Say Y, if you want to find out why the driver does not
work for you.
config B43_DMA
bool
depends on B43
config B43_PIO
bool
depends on B43
choice
prompt "Broadcom 43xx data transfer mode"
depends on B43
default B43_DMA_AND_PIO_MODE
config B43_DMA_AND_PIO_MODE
bool "DMA + PIO"
select B43_DMA
select B43_PIO
---help---
Include both, Direct Memory Access (DMA) and Programmed I/O (PIO)
data transfer modes.
The actually used mode is selectable through the module
parameter "pio". If the module parameter is pio=0, DMA is used.
Otherwise PIO is used. DMA is default.
If unsure, choose this option.
config B43_DMA_MODE
bool "DMA (Direct Memory Access) only"
select B43_DMA
---help---
Only include Direct Memory Access (DMA).
This reduces the size of the driver module, by omitting the PIO code.
config B43_PIO_MODE
bool "PIO (Programmed I/O) only"
select B43_PIO
---help---
Only include Programmed I/O (PIO).
This reduces the size of the driver module, by omitting the DMA code.
Please note that PIO transfers are slow (compared to DMA).
Also note that not all devices of the 43xx series support PIO.
The 4306 (Apple Airport Extreme and others) supports PIO, while
the 4318 is known to _not_ support PIO.
Only use PIO, if DMA does not work for you.
endchoice
# b43 core
b43-y += main.o
b43-y += tables.o
b43-y += phy.o
b43-y += sysfs.o
b43-y += leds.o
b43-y += xmit.o
b43-y += lo.o
# b43 PCMCIA support
b43-$(CONFIG_B43_PCMCIA) += pcmcia.o
# b43 debugging
b43-$(CONFIG_B43_DEBUG) += debugfs.o
# b43 DMA and PIO
b43-$(CONFIG_B43_DMA) += dma.o
b43-$(CONFIG_B43_PIO) += pio.o
obj-$(CONFIG_B43) += b43.o
This diff is collapsed.
This diff is collapsed.
#ifndef B43_DEBUGFS_H_
#define B43_DEBUGFS_H_
struct b43_wldev;
struct b43_txstatus;
enum b43_dyndbg { /* Dynamic debugging features */
B43_DBG_XMITPOWER,
B43_DBG_DMAOVERFLOW,
B43_DBG_DMAVERBOSE,
B43_DBG_PWORK_FAST,
B43_DBG_PWORK_STOP,
__B43_NR_DYNDBG,
};
#ifdef CONFIG_B43_DEBUG
struct dentry;
#define B43_NR_LOGGED_TXSTATUS 100
struct b43_txstatus_log {
struct b43_txstatus *log;
int end;
spinlock_t lock;
};
struct b43_dfs_file {
struct dentry *dentry;
char *buffer;
size_t data_len;
};
struct b43_dfsentry {
struct b43_wldev *dev;
struct dentry *subdir;
struct b43_dfs_file file_tsf;
struct b43_dfs_file file_ucode_regs;
struct b43_dfs_file file_shm;
struct b43_dfs_file file_txstat;
struct b43_dfs_file file_txpower_g;
struct b43_dfs_file file_restart;
struct b43_dfs_file file_loctls;
struct b43_txstatus_log txstatlog;
/* Enabled/Disabled list for the dynamic debugging features. */
u32 dyn_debug[__B43_NR_DYNDBG];
/* Dentries for the dynamic debugging entries. */
struct dentry *dyn_debug_dentries[__B43_NR_DYNDBG];
};
int b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature);
void b43_debugfs_init(void);
void b43_debugfs_exit(void);
void b43_debugfs_add_device(struct b43_wldev *dev);
void b43_debugfs_remove_device(struct b43_wldev *dev);
void b43_debugfs_log_txstat(struct b43_wldev *dev,
const struct b43_txstatus *status);
#else /* CONFIG_B43_DEBUG */
static inline int b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature)
{
return 0;
}
static inline void b43_debugfs_init(void)
{
}
static inline void b43_debugfs_exit(void)
{
}
static inline void b43_debugfs_add_device(struct b43_wldev *dev)
{
}
static inline void b43_debugfs_remove_device(struct b43_wldev *dev)
{
}
static inline void b43_debugfs_log_txstat(struct b43_wldev *dev,
const struct b43_txstatus *status)
{
}
#endif /* CONFIG_B43_DEBUG */
#endif /* B43_DEBUGFS_H_ */
This diff is collapsed.
This diff is collapsed.
/*
Broadcom B43 wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mb@bu3sch.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "b43.h"
#include "leds.h"
#include "main.h"
static void b43_led_changestate(struct b43_led *led)
{
struct b43_wldev *dev = led->dev;
const int index = b43_led_index(led);
const u16 mask = (1 << index);
u16 ledctl;
B43_WARN_ON(!(index >= 0 && index < B43_NR_LEDS));
B43_WARN_ON(!led->blink_interval);
ledctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
ledctl = (ledctl & mask) ? (ledctl & ~mask) : (ledctl | mask);
b43_write16(dev, B43_MMIO_GPIO_CONTROL, ledctl);
}
static void b43_led_blink(unsigned long d)
{
struct b43_led *led = (struct b43_led *)d;
struct b43_wldev *dev = led->dev;
unsigned long flags;
spin_lock_irqsave(&dev->wl->leds_lock, flags);
if (led->blink_interval) {
b43_led_changestate(led);
mod_timer(&led->blink_timer, jiffies + led->blink_interval);
}
spin_unlock_irqrestore(&dev->wl->leds_lock, flags);
}
static void b43_led_blink_start(struct b43_led *led, unsigned long interval)
{
if (led->blink_interval)
return;
led->blink_interval = interval;
b43_led_changestate(led);
led->blink_timer.expires = jiffies + interval;
add_timer(&led->blink_timer);
}
static void b43_led_blink_stop(struct b43_led *led, int sync)
{
struct b43_wldev *dev = led->dev;
const int index = b43_led_index(led);
u16 ledctl;
if (!led->blink_interval)
return;
if (unlikely(sync))
del_timer_sync(&led->blink_timer);
else
del_timer(&led->blink_timer);
led->blink_interval = 0;
/* Make sure the LED is turned off. */
B43_WARN_ON(!(index >= 0 && index < B43_NR_LEDS));
ledctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
if (led->activelow)
ledctl |= (1 << index);
else
ledctl &= ~(1 << index);
b43_write16(dev, B43_MMIO_GPIO_CONTROL, ledctl);
}
static void b43_led_init_hardcoded(struct b43_wldev *dev,
struct b43_led *led, int led_index)
{
struct ssb_bus *bus = dev->dev->bus;
/* This function is called, if the behaviour (and activelow)
* information for a LED is missing in the SPROM.
* We hardcode the behaviour values for various devices here.
* Note that the B43_LED_TEST_XXX behaviour values can
* be used to figure out which led is mapped to which index.
*/
switch (led_index) {
case 0:
led->behaviour = B43_LED_ACTIVITY;
led->activelow = 1;
if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ)
led->behaviour = B43_LED_RADIO_ALL;
break;
case 1:
led->behaviour = B43_LED_RADIO_B;
if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK)
led->behaviour = B43_LED_ASSOC;
break;
case 2:
led->behaviour = B43_LED_RADIO_A;
break;
case 3:
led->behaviour = B43_LED_OFF;
break;
default:
B43_WARN_ON(1);
}
}
int b43_leds_init(struct b43_wldev *dev)
{
struct b43_led *led;
u8 sprom[4];
int i;
sprom[0] = dev->dev->bus->sprom.r1.gpio0;
sprom[1] = dev->dev->bus->sprom.r1.gpio1;
sprom[2] = dev->dev->bus->sprom.r1.gpio2;
sprom[3] = dev->dev->bus->sprom.r1.gpio3;
for (i = 0; i < B43_NR_LEDS; i++) {
led = &(dev->leds[i]);
led->dev = dev;
setup_timer(&led->blink_timer,
b43_led_blink, (unsigned long)led);
if (sprom[i] == 0xFF) {
b43_led_init_hardcoded(dev, led, i);
} else {
led->behaviour = sprom[i] & B43_LED_BEHAVIOUR;
led->activelow = !!(sprom[i] & B43_LED_ACTIVELOW);
}
}
return 0;
}
void b43_leds_exit(struct b43_wldev *dev)
{
struct b43_led *led;
int i;
for (i = 0; i < B43_NR_LEDS; i++) {
led = &(dev->leds[i]);
b43_led_blink_stop(led, 1);
}
b43_leds_switch_all(dev, 0);
}
void b43_leds_update(struct b43_wldev *dev, int activity)
{
struct b43_led *led;
struct b43_phy *phy = &dev->phy;
const int transferring =
(jiffies - dev->stats.last_tx) < B43_LED_XFER_THRES;
int i, turn_on;
unsigned long interval = 0;
u16 ledctl;
unsigned long flags;
spin_lock_irqsave(&dev->wl->leds_lock, flags);
ledctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
for (i = 0; i < B43_NR_LEDS; i++) {
led = &(dev->leds[i]);
turn_on = 0;
switch (led->behaviour) {
case B43_LED_INACTIVE:
continue;
case B43_LED_OFF:
break;
case B43_LED_ON:
turn_on = 1;
break;
case B43_LED_ACTIVITY:
turn_on = activity;
break;
case B43_LED_RADIO_ALL:
turn_on = phy->radio_on && b43_is_hw_radio_enabled(dev);
break;
case B43_LED_RADIO_A:
turn_on = (phy->radio_on && b43_is_hw_radio_enabled(dev)
&& phy->type == B43_PHYTYPE_A);
break;
case B43_LED_RADIO_B:
turn_on = (phy->radio_on && b43_is_hw_radio_enabled(dev)
&& (phy->type == B43_PHYTYPE_B
|| phy->type == B43_PHYTYPE_G));
break;
case B43_LED_MODE_BG:
if (phy->type == B43_PHYTYPE_G
&& b43_is_hw_radio_enabled(dev)
&& 1 /*FIXME: using G rates. */ )
turn_on = 1;
break;
case B43_LED_TRANSFER:
if (transferring)
b43_led_blink_start(led, B43_LEDBLINK_MEDIUM);
else
b43_led_blink_stop(led, 0);
continue;
case B43_LED_APTRANSFER:
if (b43_is_mode(dev->wl, IEEE80211_IF_TYPE_AP)) {
if (transferring) {
interval = B43_LEDBLINK_FAST;
turn_on = 1;
}
} else {
turn_on = 1;
if (0 /*TODO: not assoc */ )
interval = B43_LEDBLINK_SLOW;
else if (transferring)
interval = B43_LEDBLINK_FAST;
else
turn_on = 0;
}
if (turn_on)
b43_led_blink_start(led, interval);
else
b43_led_blink_stop(led, 0);
continue;
case B43_LED_WEIRD:
//TODO
break;
case B43_LED_ASSOC:
if (1 /*dev->softmac->associated */ )
turn_on = 1;
break;
#ifdef CONFIG_B43_DEBUG
case B43_LED_TEST_BLINKSLOW:
b43_led_blink_start(led, B43_LEDBLINK_SLOW);
continue;
case B43_LED_TEST_BLINKMEDIUM:
b43_led_blink_start(led, B43_LEDBLINK_MEDIUM);
continue;
case B43_LED_TEST_BLINKFAST:
b43_led_blink_start(led, B43_LEDBLINK_FAST);
continue;
#endif /* CONFIG_B43_DEBUG */
default:
B43_WARN_ON(1);
};
if (led->activelow)
turn_on = !turn_on;
if (turn_on)
ledctl |= (1 << i);
else
ledctl &= ~(1 << i);
}
b43_write16(dev, B43_MMIO_GPIO_CONTROL, ledctl);
spin_unlock_irqrestore(&dev->wl->leds_lock, flags);
}
void b43_leds_switch_all(struct b43_wldev *dev, int on)
{
struct b43_led *led;
u16 ledctl;
int i;
int bit_on;
unsigned long flags;
spin_lock_irqsave(&dev->wl->leds_lock, flags);
ledctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
for (i = 0; i < B43_NR_LEDS; i++) {
led = &(dev->leds[i]);
if (led->behaviour == B43_LED_INACTIVE)
continue;
if (on)
bit_on = led->activelow ? 0 : 1;
else
bit_on = led->activelow ? 1 : 0;
if (bit_on)
ledctl |= (1 << i);
else
ledctl &= ~(1 << i);
}
b43_write16(dev, B43_MMIO_GPIO_CONTROL, ledctl);
spin_unlock_irqrestore(&dev->wl->leds_lock, flags);
}
#ifndef B43_LEDS_H_
#define B43_LEDS_H_
#include <linux/types.h>
#include <linux/timer.h>
struct b43_led {
u8 behaviour:7;
u8 activelow:1;
struct b43_wldev *dev;
struct timer_list blink_timer;
unsigned long blink_interval;
};
#define b43_led_index(led) ((int)((led) - (led)->dev->leds))
/* Delay between state changes when blinking in jiffies */
#define B43_LEDBLINK_SLOW (HZ / 1)
#define B43_LEDBLINK_MEDIUM (HZ / 4)
#define B43_LEDBLINK_FAST (HZ / 8)
#define B43_LED_XFER_THRES (HZ / 100)
#define B43_LED_BEHAVIOUR 0x7F
#define B43_LED_ACTIVELOW 0x80
enum { /* LED behaviour values */
B43_LED_OFF,
B43_LED_ON,
B43_LED_ACTIVITY,
B43_LED_RADIO_ALL,
B43_LED_RADIO_A,
B43_LED_RADIO_B,
B43_LED_MODE_BG,
B43_LED_TRANSFER,
B43_LED_APTRANSFER,
B43_LED_WEIRD, //FIXME
B43_LED_ASSOC,
B43_LED_INACTIVE,
/* Behaviour values for testing.
* With these values it is easier to figure out
* the real behaviour of leds, in case the SPROM
* is missing information.
*/
B43_LED_TEST_BLINKSLOW,
B43_LED_TEST_BLINKMEDIUM,
B43_LED_TEST_BLINKFAST,
};
int b43_leds_init(struct b43_wldev *dev);
void b43_leds_exit(struct b43_wldev *dev);
void b43_leds_update(struct b43_wldev *dev, int activity);
void b43_leds_switch_all(struct b43_wldev *dev, int on);
#endif /* B43_LEDS_H_ */
This diff is collapsed.
#ifndef B43_LO_H_
#define B43_LO_H_
#include "phy.h"
struct b43_wldev;
/* Local Oscillator control value-pair. */
struct b43_loctl {
/* Control values. */
s8 i;
s8 q;
/* "Used by hardware" flag. */
bool used;
#ifdef CONFIG_B43_DEBUG
/* Is this lo-control-array entry calibrated? */
bool calibrated;
#endif
};
/* Debugging: Poison value for i and q values. */
#define B43_LOCTL_POISON 111
/* loctl->calibrated debugging mechanism */
#ifdef CONFIG_B43_DEBUG
static inline void b43_loctl_set_calibrated(struct b43_loctl *loctl,
bool calibrated)
{
loctl->calibrated = calibrated;
}
static inline bool b43_loctl_is_calibrated(struct b43_loctl *loctl)
{
return loctl->calibrated;
}
#else
static inline void b43_loctl_set_calibrated(struct b43_loctl *loctl,
bool calibrated)
{
}
static inline bool b43_loctl_is_calibrated(struct b43_loctl *loctl)
{
return 1;
}
#endif
/* TX Power LO Control Array.
* Value-pairs to adjust the LocalOscillator are stored
* in this structure.
* There are two different set of values. One for "Flag is Set"
* and one for "Flag is Unset".
* By "Flag" the flag in struct b43_rfatt is meant.
* The Value arrays are two-dimensional. The first index
* is the baseband attenuation and the second index
* is the radio attenuation.
* Use b43_get_lo_g_ctl() to retrieve a value from the lists.
*/
struct b43_txpower_lo_control {
#define B43_NR_BB 12
#define B43_NR_RF 16
/* LO Control values, with PAD Mixer */
struct b43_loctl with_padmix[B43_NR_BB][B43_NR_RF];
/* LO Control values, without PAD Mixer */
struct b43_loctl no_padmix[B43_NR_BB][B43_NR_RF];
/* Flag to indicate a complete rebuild of the two tables above
* to the LO measuring code. */
bool rebuild;
/* Lists of valid RF and BB attenuation values for this device. */
struct b43_rfatt_list rfatt_list;
struct b43_bbatt_list bbatt_list;
/* Current TX Bias value */
u8 tx_bias;
/* Current TX Magnification Value (if used by the device) */
u8 tx_magn;
/* GPHY LO is measured. */
bool lo_measured;
/* Saved device PowerVector */
u64 power_vector;
};
/* Measure the BPHY Local Oscillator. */
void b43_lo_b_measure(struct b43_wldev *dev);
/* Measure the BPHY/GPHY Local Oscillator. */
void b43_lo_g_measure(struct b43_wldev *dev);
/* Adjust the Local Oscillator to the saved attenuation
* and txctl values.
*/
void b43_lo_g_adjust(struct b43_wldev *dev);
/* Adjust to specific values. */
void b43_lo_g_adjust_to(struct b43_wldev *dev,
u16 rfatt, u16 bbatt, u16 tx_control);
/* Mark all possible b43_lo_g_ctl as "unused" */
void b43_lo_g_ctl_mark_all_unused(struct b43_wldev *dev);
/* Mark the b43_lo_g_ctl corresponding to the current
* attenuation values as used.
*/
void b43_lo_g_ctl_mark_cur_used(struct b43_wldev *dev);
/* Get a reference to a LO Control value pair in the
* TX Power LO Control Array.
*/
struct b43_loctl *b43_get_lo_g_ctl(struct b43_wldev *dev,
const struct b43_rfatt *rfatt,