EXTRA_KCONFIG:= \
CONFIG_B43=m \
CONFIG_B43_NPHY=y \
+ CONFIG_B43_DEBUG=y \
+ $(if $(CONFIG_RFKILL),CONFIG_B43_RFKILL=y) \
$(if $(CONFIG_LEDS_TRIGGERS),CONFIG_B43_LEDS=y) \
b43-y += main.o
b43-y += tables.o
-b43-y += tables_nphy.o
+b43-$(CONFIG_B43_NPHY) += tables_nphy.o
b43-y += phy.o
-b43-y += nphy.o
+b43-$(CONFIG_B43_NPHY) += nphy.o
b43-y += sysfs.o
b43-y += xmit.o
b43-y += lo.o
b43-y += wa.o
b43-y += dma.o
+b43-$(CONFIG_B43_PIO) += pio.o
b43-$(CONFIG_B43_RFKILL) += rfkill.o
b43-$(CONFIG_B43_LEDS) += leds.o
b43-$(CONFIG_B43_PCMCIA) += pcmcia.o
#include "lo.h"
#include "phy.h"
+
+/* The unique identifier of the firmware that's officially supported by
+ * this driver version. */
+#define B43_SUPPORTED_FIRMWARE_ID "FW13"
+
+
#ifdef CONFIG_B43_DEBUG
# define B43_DEBUG 1
#else
#define B43_MMIO_DMA64_BASE4 0x300
#define B43_MMIO_DMA64_BASE5 0x340
+/* PIO on core rev < 11 */
+#define B43_MMIO_PIO_BASE0 0x300
+#define B43_MMIO_PIO_BASE1 0x310
+#define B43_MMIO_PIO_BASE2 0x320
+#define B43_MMIO_PIO_BASE3 0x330
+#define B43_MMIO_PIO_BASE4 0x340
+#define B43_MMIO_PIO_BASE5 0x350
+#define B43_MMIO_PIO_BASE6 0x360
+#define B43_MMIO_PIO_BASE7 0x370
+/* PIO on core rev >= 11 */
+#define B43_MMIO_PIO11_BASE0 0x200
+#define B43_MMIO_PIO11_BASE1 0x240
+#define B43_MMIO_PIO11_BASE2 0x280
+#define B43_MMIO_PIO11_BASE3 0x2C0
+#define B43_MMIO_PIO11_BASE4 0x300
+#define B43_MMIO_PIO11_BASE5 0x340
+
#define B43_MMIO_PHY_VER 0x3E0
#define B43_MMIO_PHY_RADIO 0x3E2
#define B43_MMIO_PHY0 0x3E6
#define B43_MMIO_GPIO_MASK 0x49E
#define B43_MMIO_TSF_CFP_START_LOW 0x604
#define B43_MMIO_TSF_CFP_START_HIGH 0x606
+#define B43_MMIO_TSF_CFP_PRETBTT 0x612
#define B43_MMIO_TSF_0 0x632 /* core rev < 3 only */
#define B43_MMIO_TSF_1 0x634 /* core rev < 3 only */
#define B43_MMIO_TSF_2 0x636 /* core rev < 3 only */
#define B43_MMIO_TSF_3 0x638 /* core rev < 3 only */
#define B43_MMIO_RNG 0x65A
+#define B43_MMIO_IFSCTL 0x688 /* Interframe space control */
+#define B43_MMIO_IFSCTL_USE_EDCF 0x0004
#define B43_MMIO_POWERUP_DELAY 0x6A8
/* SPROM boardflags_lo values */
#define B43_SHM_SH_PHYTYPE 0x0052 /* PHY type */
#define B43_SHM_SH_ANTSWAP 0x005C /* Antenna swap threshold */
#define B43_SHM_SH_HOSTFLO 0x005E /* Hostflags for ucode options (low) */
-#define B43_SHM_SH_HOSTFHI 0x0060 /* Hostflags for ucode options (high) */
+#define B43_SHM_SH_HOSTFMI 0x0060 /* Hostflags for ucode options (middle) */
+#define B43_SHM_SH_HOSTFHI 0x0062 /* Hostflags for ucode options (high) */
#define B43_SHM_SH_RFATT 0x0064 /* Current radio attenuation value */
#define B43_SHM_SH_RADAR 0x0066 /* Radar register */
#define B43_SHM_SH_PHYTXNOI 0x006E /* PHY noise directly after TX (lower 8bit only) */
#define B43_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4)
/* HostFlags. See b43_hf_read/write() */
-#define B43_HF_ANTDIVHELP 0x00000001 /* ucode antenna div helper */
-#define B43_HF_SYMW 0x00000002 /* G-PHY SYM workaround */
-#define B43_HF_RXPULLW 0x00000004 /* RX pullup workaround */
-#define B43_HF_CCKBOOST 0x00000008 /* 4dB CCK power boost (exclusive with OFDM boost) */
-#define B43_HF_BTCOEX 0x00000010 /* Bluetooth coexistance */
-#define B43_HF_GDCW 0x00000020 /* G-PHY DV canceller filter bw workaround */
-#define B43_HF_OFDMPABOOST 0x00000040 /* Enable PA gain boost for OFDM */
-#define B43_HF_ACPR 0x00000080 /* Disable for Japan, channel 14 */
-#define B43_HF_EDCF 0x00000100 /* on if WME and MAC suspended */
-#define B43_HF_TSSIRPSMW 0x00000200 /* TSSI reset PSM ucode workaround */
-#define B43_HF_DSCRQ 0x00000400 /* Disable slow clock request in ucode */
-#define B43_HF_ACIW 0x00000800 /* ACI workaround: shift bits by 2 on PHY CRS */
-#define B43_HF_2060W 0x00001000 /* 2060 radio workaround */
-#define B43_HF_RADARW 0x00002000 /* Radar workaround */
-#define B43_HF_USEDEFKEYS 0x00004000 /* Enable use of default keys */
-#define B43_HF_BT4PRIOCOEX 0x00010000 /* Bluetooth 2-priority coexistance */
-#define B43_HF_FWKUP 0x00020000 /* Fast wake-up ucode */
-#define B43_HF_VCORECALC 0x00040000 /* Force VCO recalculation when powering up synthpu */
-#define B43_HF_PCISCW 0x00080000 /* PCI slow clock workaround */
-#define B43_HF_4318TSSI 0x00200000 /* 4318 TSSI */
-#define B43_HF_FBCMCFIFO 0x00400000 /* Flush bcast/mcast FIFO immediately */
-#define B43_HF_HWPCTL 0x00800000 /* Enable hardwarre power control */
-#define B43_HF_BTCOEXALT 0x01000000 /* Bluetooth coexistance in alternate pins */
-#define B43_HF_TXBTCHECK 0x02000000 /* Bluetooth check during transmission */
-#define B43_HF_SKCFPUP 0x04000000 /* Skip CFP update */
+#define B43_HF_ANTDIVHELP 0x000000000001ULL /* ucode antenna div helper */
+#define B43_HF_SYMW 0x000000000002ULL /* G-PHY SYM workaround */
+#define B43_HF_RXPULLW 0x000000000004ULL /* RX pullup workaround */
+#define B43_HF_CCKBOOST 0x000000000008ULL /* 4dB CCK power boost (exclusive with OFDM boost) */
+#define B43_HF_BTCOEX 0x000000000010ULL /* Bluetooth coexistance */
+#define B43_HF_GDCW 0x000000000020ULL /* G-PHY DC canceller filter bw workaround */
+#define B43_HF_OFDMPABOOST 0x000000000040ULL /* Enable PA gain boost for OFDM */
+#define B43_HF_ACPR 0x000000000080ULL /* Disable for Japan, channel 14 */
+#define B43_HF_EDCF 0x000000000100ULL /* on if WME and MAC suspended */
+#define B43_HF_TSSIRPSMW 0x000000000200ULL /* TSSI reset PSM ucode workaround */
+#define B43_HF_20IN40IQW 0x000000000200ULL /* 20 in 40 MHz I/Q workaround (rev >= 13 only) */
+#define B43_HF_DSCRQ 0x000000000400ULL /* Disable slow clock request in ucode */
+#define B43_HF_ACIW 0x000000000800ULL /* ACI workaround: shift bits by 2 on PHY CRS */
+#define B43_HF_2060W 0x000000001000ULL /* 2060 radio workaround */
+#define B43_HF_RADARW 0x000000002000ULL /* Radar workaround */
+#define B43_HF_USEDEFKEYS 0x000000004000ULL /* Enable use of default keys */
+#define B43_HF_AFTERBURNER 0x000000008000ULL /* Afterburner enabled */
+#define B43_HF_BT4PRIOCOEX 0x000000010000ULL /* Bluetooth 4-priority coexistance */
+#define B43_HF_FWKUP 0x000000020000ULL /* Fast wake-up ucode */
+#define B43_HF_VCORECALC 0x000000040000ULL /* Force VCO recalculation when powering up synthpu */
+#define B43_HF_PCISCW 0x000000080000ULL /* PCI slow clock workaround */
+#define B43_HF_4318TSSI 0x000000200000ULL /* 4318 TSSI */
+#define B43_HF_FBCMCFIFO 0x000000400000ULL /* Flush bcast/mcast FIFO immediately */
+#define B43_HF_HWPCTL 0x000000800000ULL /* Enable hardwarre power control */
+#define B43_HF_BTCOEXALT 0x000001000000ULL /* Bluetooth coexistance in alternate pins */
+#define B43_HF_TXBTCHECK 0x000002000000ULL /* Bluetooth check during transmission */
+#define B43_HF_SKCFPUP 0x000004000000ULL /* Skip CFP update */
+#define B43_HF_N40W 0x000008000000ULL /* N PHY 40 MHz workaround (rev >= 13 only) */
+#define B43_HF_ANTSEL 0x000020000000ULL /* Antenna selection (for testing antenna div.) */
+#define B43_HF_BT3COEXT 0x000020000000ULL /* Bluetooth 3-wire coexistence (rev >= 13 only) */
+#define B43_HF_BTCANT 0x000040000000ULL /* Bluetooth coexistence (antenna mode) (rev >= 13 only) */
+#define B43_HF_ANTSELEN 0x000100000000ULL /* Antenna selection enabled (rev >= 13 only) */
+#define B43_HF_ANTSELMODE 0x000200000000ULL /* Antenna selection mode (rev >= 13 only) */
+#define B43_HF_MLADVW 0x001000000000ULL /* N PHY ML ADV workaround (rev >= 13 only) */
+#define B43_HF_PR45960W 0x080000000000ULL /* PR 45960 workaround (rev >= 13 only) */
/* MacFilter offsets. */
#define B43_MACFILTER_SELF 0x0000
#define B43_IRQ_TIMEOUT 0x80000000
#define B43_IRQ_ALL 0xFFFFFFFF
-#define B43_IRQ_MASKTEMPLATE (B43_IRQ_MAC_SUSPENDED | \
- B43_IRQ_BEACON | \
- B43_IRQ_TBTT_INDI | \
+#define B43_IRQ_MASKTEMPLATE (B43_IRQ_TBTT_INDI | \
B43_IRQ_ATIM_END | \
B43_IRQ_PMQ | \
B43_IRQ_MAC_TXERR | \
B43_IRQ_RFKILL | \
B43_IRQ_TX_OK)
+/* The firmware register to fetch the debug-IRQ reason from. */
+#define B43_DEBUGIRQ_REASON_REG 63
+/* Debug-IRQ reasons. */
+#define B43_DEBUGIRQ_PANIC 0 /* The firmware panic'ed */
+#define B43_DEBUGIRQ_DUMP_SHM 1 /* Dump shared SHM */
+#define B43_DEBUGIRQ_DUMP_REGS 2 /* Dump the microcode registers */
+#define B43_DEBUGIRQ_MARKER 3 /* A "marker" was thrown by the firmware. */
+#define B43_DEBUGIRQ_ACK 0xFFFF /* The host writes that to ACK the IRQ */
+
+/* The firmware register that contains the "marker" line. */
+#define B43_MARKER_ID_REG 2
+#define B43_MARKER_LINE_REG 3
+
+/* The firmware register to fetch the panic reason from. */
+#define B43_FWPANIC_REASON_REG 3
+/* Firmware panic reason codes */
+#define B43_FWPANIC_DIE 0 /* Firmware died. Don't auto-restart it. */
+#define B43_FWPANIC_RESTART 1 /* Firmware died. Schedule a controller reset. */
+
+
/* Device specific rate values.
* The actual values defined here are (rate_in_mbps * 2).
* Some code depends on this. Don't change it. */
};
struct b43_dmaring;
-struct b43_pioqueue;
/* The firmware file header */
#define B43_FW_TYPE_UCODE 'u'
} __attribute__((__packed__));
-#define B43_PHYMODE(phytype) (1 << (phytype))
-#define B43_PHYMODE_A B43_PHYMODE(B43_PHYTYPE_A)
-#define B43_PHYMODE_B B43_PHYMODE(B43_PHYTYPE_B)
-#define B43_PHYMODE_G B43_PHYMODE(B43_PHYTYPE_G)
-
struct b43_phy {
- /* Possible PHYMODEs on this PHY */
- u8 possible_phymodes;
+ /* Band support flags. */
+ bool supports_2ghz;
+ bool supports_5ghz;
+
/* GMODE bit enabled? */
bool gmode;
/* Data structures for DMA transmission, per 80211 core. */
struct b43_dma {
- struct b43_dmaring *tx_ring0;
- struct b43_dmaring *tx_ring1;
- struct b43_dmaring *tx_ring2;
- struct b43_dmaring *tx_ring3;
- struct b43_dmaring *tx_ring4;
- struct b43_dmaring *tx_ring5;
-
- struct b43_dmaring *rx_ring0;
- struct b43_dmaring *rx_ring3; /* only available on core.rev < 5 */
+ struct b43_dmaring *tx_ring_AC_BK; /* Background */
+ struct b43_dmaring *tx_ring_AC_BE; /* Best Effort */
+ struct b43_dmaring *tx_ring_AC_VI; /* Video */
+ struct b43_dmaring *tx_ring_AC_VO; /* Voice */
+ struct b43_dmaring *tx_ring_mcast; /* Multicast */
+
+ struct b43_dmaring *rx_ring;
+};
+
+struct b43_pio_txqueue;
+struct b43_pio_rxqueue;
+
+/* Data structures for PIO transmission, per 80211 core. */
+struct b43_pio {
+ struct b43_pio_txqueue *tx_queue_AC_BK; /* Background */
+ struct b43_pio_txqueue *tx_queue_AC_BE; /* Best Effort */
+ struct b43_pio_txqueue *tx_queue_AC_VI; /* Video */
+ struct b43_pio_txqueue *tx_queue_AC_VO; /* Voice */
+ struct b43_pio_txqueue *tx_queue_mcast; /* Multicast */
+
+ struct b43_pio_rxqueue *rx_queue;
};
/* Context information for a noise calculation (Link Quality). */
u8 algorithm;
};
+/* SHM offsets to the QOS data structures for the 4 different queues. */
+#define B43_QOS_PARAMS(queue) (B43_SHM_SH_EDCFQ + \
+ (B43_NR_QOSPARAMS * sizeof(u16) * (queue)))
+#define B43_QOS_BACKGROUND B43_QOS_PARAMS(0)
+#define B43_QOS_BESTEFFORT B43_QOS_PARAMS(1)
+#define B43_QOS_VIDEO B43_QOS_PARAMS(2)
+#define B43_QOS_VOICE B43_QOS_PARAMS(3)
+
+/* QOS parameter hardware data structure offsets. */
+#define B43_NR_QOSPARAMS 22
+enum {
+ B43_QOSPARAM_TXOP = 0,
+ B43_QOSPARAM_CWMIN,
+ B43_QOSPARAM_CWMAX,
+ B43_QOSPARAM_CWCUR,
+ B43_QOSPARAM_AIFS,
+ B43_QOSPARAM_BSLOTS,
+ B43_QOSPARAM_REGGAP,
+ B43_QOSPARAM_STATUS,
+};
+
+/* QOS parameters for a queue. */
+struct b43_qos_params {
+ /* The QOS parameters */
+ struct ieee80211_tx_queue_params p;
+ /* Does this need to get uploaded to hardware? */
+ bool need_hw_update;
+};
+
struct b43_wldev;
/* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */
struct mutex mutex;
spinlock_t irq_lock;
+ /* R/W lock for data transmission.
+ * Transmissions on 2+ queues can run concurrently, but somebody else
+ * might sync with TX by write_lock_irqsave()'ing. */
+ rwlock_t tx_lock;
/* Lock for LEDs access. */
spinlock_t leds_lock;
/* Lock for SHM access. */
struct sk_buff *current_beacon;
bool beacon0_uploaded;
bool beacon1_uploaded;
+ struct work_struct beacon_update_trigger;
+
+ /* The current QOS parameters for the 4 queues.
+ * This is protected by the irq_lock. */
+ struct b43_qos_params qos_params[4];
+ /* Workqueue for updating QOS parameters in hardware. */
+ struct work_struct qos_update_work;
};
/* In-memory representation of a cached microcode file. */
u16 rev;
/* Firmware patchlevel */
u16 patch;
+
+ /* Set to true, if we are using an opensource firmware. */
+ bool opensource;
+ /* Set to true, if the core needs a PCM firmware, but
+ * we failed to load one. This is always false for
+ * core rev > 10, as these don't need PCM firmware. */
+ bool pcm_request_failed;
};
/* Device (802.11 core) initialization status. */
bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM) */
bool short_slot; /* TRUE, if short slot timing is enabled. */
bool radio_hw_enable; /* saved state of radio hardware enabled state */
+ bool suspend_in_progress; /* TRUE, if we are in a suspend/resume cycle */
/* PHY/Radio device. */
struct b43_phy phy;
- /* DMA engines. */
- struct b43_dma dma;
+ union {
+ /* DMA engines. */
+ struct b43_dma dma;
+ /* PIO engines. */
+ struct b43_pio pio;
+ };
+ /* Use b43_using_pio_transfers() to check whether we are using
+ * DMA or PIO data transfers. */
+ bool __using_pio_transfers;
/* Various statistics about the physical device. */
struct b43_stats stats;
ssb_write32(dev->dev, offset, value);
}
+static inline bool b43_using_pio_transfers(struct b43_wldev *dev)
+{
+#ifdef CONFIG_B43_PIO
+ return dev->__using_pio_transfers;
+#else
+ return 0;
+#endif
+}
+
+#ifdef CONFIG_B43_FORCE_PIO
+# define B43_FORCE_PIO 1
+#else
+# define B43_FORCE_PIO 0
+#endif
+
+
/* Message printing */
void b43info(struct b43_wl *wl, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
# define B43_WARN_ON(x) __b43_warn_on_dummy(unlikely(!!(x)))
#endif
-/** Limit a value between two limits */
-#ifdef limit_value
-# undef limit_value
-#endif
-#define limit_value(value, min, max) \
- ({ \
- typeof(value) __value = (value); \
- typeof(value) __min = (min); \
- typeof(value) __max = (max); \
- if (__value < __min) \
- __value = __min; \
- else if (__value > __max) \
- __value = __max; \
- __value; \
- })
-
/* Convert an integer to a Q5.2 value */
#define INT_TO_Q52(i) ((i) << 2)
/* Convert a Q5.2 value to an integer (precision loss!) */
return err;
}
-static ssize_t append_lo_table(ssize_t count, char *buf, const size_t bufsize,
- struct b43_loctl table[B43_NR_BB][B43_NR_RF])
+static unsigned long calc_expire_secs(unsigned long now,
+ unsigned long time,
+ unsigned long expire)
{
- unsigned int i, j;
- struct b43_loctl *ctl;
-
- for (i = 0; i < B43_NR_BB; i++) {
- for (j = 0; j < B43_NR_RF; j++) {
- ctl = &(table[i][j]);
- fappend("(bbatt %2u, rfatt %2u) -> "
- "(I %+3d, Q %+3d, Used: %d, Calibrated: %d)\n",
- i, j, ctl->i, ctl->q,
- ctl->used,
- b43_loctl_is_calibrated(ctl));
- }
+ expire = time + expire;
+
+ if (time_after(now, expire))
+ return 0; /* expired */
+ if (expire < now) {
+ /* jiffies wrapped */
+ expire -= MAX_JIFFY_OFFSET;
+ now -= MAX_JIFFY_OFFSET;
}
+ B43_WARN_ON(expire < now);
- return count;
+ return (expire - now) / HZ;
}
static ssize_t loctls_read_file(struct b43_wldev *dev,
ssize_t count = 0;
struct b43_txpower_lo_control *lo;
int i, err = 0;
+ struct b43_lo_calib *cal;
+ unsigned long now = jiffies;
+ struct b43_phy *phy = &dev->phy;
- if (dev->phy.type != B43_PHYTYPE_G) {
+ if (phy->type != B43_PHYTYPE_G) {
fappend("Device is not a G-PHY\n");
err = -ENODEV;
goto out;
}
- lo = dev->phy.lo_control;
+ lo = phy->lo_control;
fappend("-- Local Oscillator calibration data --\n\n");
- fappend("Measured: %d, Rebuild: %d, HW-power-control: %d\n",
- lo->lo_measured,
- lo->rebuild,
+ fappend("HW-power-control enabled: %d\n",
dev->phy.hardware_power_control);
- fappend("TX Bias: 0x%02X, TX Magn: 0x%02X\n",
- lo->tx_bias, lo->tx_magn);
- fappend("Power Vector: 0x%08X%08X\n",
+ fappend("TX Bias: 0x%02X, TX Magn: 0x%02X (expire in %lu sec)\n",
+ lo->tx_bias, lo->tx_magn,
+ calc_expire_secs(now, lo->txctl_measured_time,
+ B43_LO_TXCTL_EXPIRE));
+ fappend("Power Vector: 0x%08X%08X (expires in %lu sec)\n",
(unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32),
- (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL));
- fappend("\nControl table WITH PADMIX:\n");
- count = append_lo_table(count, buf, bufsize, lo->with_padmix);
- fappend("\nControl table WITHOUT PADMIX:\n");
- count = append_lo_table(count, buf, bufsize, lo->no_padmix);
+ (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL),
+ calc_expire_secs(now, lo->pwr_vec_read_time,
+ B43_LO_PWRVEC_EXPIRE));
+
+ fappend("\nCalibrated settings:\n");
+ list_for_each_entry(cal, &lo->calib_list, list) {
+ bool active;
+
+ active = (b43_compare_bbatt(&cal->bbatt, &phy->bbatt) &&
+ b43_compare_rfatt(&cal->rfatt, &phy->rfatt));
+ fappend("BB(%d), RF(%d,%d) -> I=%d, Q=%d "
+ "(expires in %lu sec)%s\n",
+ cal->bbatt.att,
+ cal->rfatt.att, cal->rfatt.with_padmix,
+ cal->ctl.i, cal->ctl.q,
+ calc_expire_secs(now, cal->calib_time,
+ B43_LO_CALIB_EXPIRE),
+ active ? " ACTIVE" : "");
+ }
+
fappend("\nUsed RF attenuation values: Value(WithPadmix flag)\n");
for (i = 0; i < lo->rfatt_list.len; i++) {
fappend("%u(%d), ",
struct b43_dfs_file *dfile;
ssize_t uninitialized_var(ret);
char *buf;
- const size_t bufsize = 1024 * 128;
+ const size_t bufsize = 1024 * 16; /* 16 kiB buffer */
const size_t buforder = get_order(bufsize);
int err = 0;
err = -ENOMEM;
goto out_unlock;
}
- /* Sparse warns about the following memset, because it has a big
- * size value. That warning is bogus, so I will ignore it. --mb */
memset(buf, 0, bufsize);
if (dfops->take_irqlock) {
spin_lock_irq(&dev->wl->irq_lock);
add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, 0);
add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, 0);
add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, 0);
+ add_dyn_dbg("debug_lo", B43_DBG_LO, 0);
#undef add_dyn_dbg
}
kfree(e);
}
+/* Called with IRQs disabled. */
void b43_debugfs_log_txstat(struct b43_wldev *dev,
const struct b43_txstatus *status)
{
if (!e)
return;
log = &e->txstatlog;
- B43_WARN_ON(!irqs_disabled());
- spin_lock(&log->lock);
+ spin_lock(&log->lock); /* IRQs are already disabled. */
i = log->end + 1;
if (i == B43_NR_LOGGED_TXSTATUS)
i = 0;
B43_DBG_DMAVERBOSE,
B43_DBG_PWORK_FAST,
B43_DBG_PWORK_STOP,
+ B43_DBG_LO,
__B43_NR_DYNDBG,
};
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
+#include <asm/div64.h>
/* 32bit DMA ops. */
return slot;
}
-/* Mac80211-queue to b43-ring mapping */
-static struct b43_dmaring *priority_to_txring(struct b43_wldev *dev,
- int queue_priority)
-{
- struct b43_dmaring *ring;
-
-/*FIXME: For now we always run on TX-ring-1 */
- return dev->dma.tx_ring1;
-
- /* 0 = highest priority */
- switch (queue_priority) {
- default:
- B43_WARN_ON(1);
- /* fallthrough */
- case 0:
- ring = dev->dma.tx_ring3;
- break;
- case 1:
- ring = dev->dma.tx_ring2;
- break;
- case 2:
- ring = dev->dma.tx_ring1;
- break;
- case 3:
- ring = dev->dma.tx_ring0;
- break;
- }
-
- return ring;
-}
-
-/* b43-ring to mac80211-queue mapping */
-static inline int txring_to_priority(struct b43_dmaring *ring)
-{
- static const u8 idx_to_prio[] = { 3, 2, 1, 0, };
- unsigned int index;
-
-/*FIXME: have only one queue, for now */
- return 0;
-
- index = ring->index;
- if (B43_WARN_ON(index >= ARRAY_SIZE(idx_to_prio)))
- index = 0;
- return idx_to_prio[index];
-}
-
static u16 b43_dmacontroller_base(enum b43_dmatype type, int controller_idx)
{
static const u16 map64[] = {
dma_addr_t dmaaddr;
if (tx) {
- dmaaddr = dma_map_single(ring->dev->dev->dev,
+ dmaaddr = dma_map_single(ring->dev->dev->dma_dev,
buf, len, DMA_TO_DEVICE);
} else {
- dmaaddr = dma_map_single(ring->dev->dev->dev,
+ dmaaddr = dma_map_single(ring->dev->dev->dma_dev,
buf, len, DMA_FROM_DEVICE);
}
dma_addr_t addr, size_t len, int tx)
{
if (tx) {
- dma_unmap_single(ring->dev->dev->dev, addr, len, DMA_TO_DEVICE);
+ dma_unmap_single(ring->dev->dev->dma_dev,
+ addr, len, DMA_TO_DEVICE);
} else {
- dma_unmap_single(ring->dev->dev->dev,
+ dma_unmap_single(ring->dev->dev->dma_dev,
addr, len, DMA_FROM_DEVICE);
}
}
dma_addr_t addr, size_t len)
{
B43_WARN_ON(ring->tx);
- dma_sync_single_for_cpu(ring->dev->dev->dev,
+ dma_sync_single_for_cpu(ring->dev->dev->dma_dev,
addr, len, DMA_FROM_DEVICE);
}
dma_addr_t addr, size_t len)
{
B43_WARN_ON(ring->tx);
- dma_sync_single_for_device(ring->dev->dev->dev,
+ dma_sync_single_for_device(ring->dev->dev->dma_dev,
addr, len, DMA_FROM_DEVICE);
}
static int alloc_ringmemory(struct b43_dmaring *ring)
{
- struct device *dev = ring->dev->dev->dev;
+ struct device *dma_dev = ring->dev->dev->dma_dev;
gfp_t flags = GFP_KERNEL;
/* The specs call for 4K buffers for 30- and 32-bit DMA with 4K
*/
if (ring->type == B43_DMA_64BIT)
flags |= GFP_DMA;
- ring->descbase = dma_alloc_coherent(dev, B43_DMA_RINGMEMSIZE,
+ ring->descbase = dma_alloc_coherent(dma_dev, B43_DMA_RINGMEMSIZE,
&(ring->dmabase), flags);
if (!ring->descbase) {
b43err(ring->dev->wl, "DMA ringmemory allocation failed\n");
static void free_ringmemory(struct b43_dmaring *ring)
{
- struct device *dev = ring->dev->dev->dev;
+ struct device *dma_dev = ring->dev->dev->dma_dev;
- dma_free_coherent(dev, B43_DMA_RINGMEMSIZE,
+ dma_free_coherent(dma_dev, B43_DMA_RINGMEMSIZE,
ring->descbase, ring->dmabase);
}
/* Check if a DMA mapping address is invalid. */
static bool b43_dma_mapping_error(struct b43_dmaring *ring,
dma_addr_t addr,
- size_t buffersize)
+ size_t buffersize, bool dma_to_device)
{
if (unlikely(dma_mapping_error(addr)))
return 1;
switch (ring->type) {
case B43_DMA_30BIT:
if ((u64)addr + buffersize > (1ULL << 30))
- return 1;
+ goto address_error;
break;
case B43_DMA_32BIT:
if ((u64)addr + buffersize > (1ULL << 32))
- return 1;
+ goto address_error;
break;
case B43_DMA_64BIT:
/* Currently we can't have addresses beyond
/* The address is OK. */
return 0;
+
+address_error:
+ /* We can't support this address. Unmap it again. */
+ unmap_descbuffer(ring, addr, buffersize, dma_to_device);
+
+ return 1;
}
static int setup_rx_descbuffer(struct b43_dmaring *ring,
struct b43_dmadesc_meta *meta, gfp_t gfp_flags)
{
struct b43_rxhdr_fw4 *rxhdr;
- struct b43_hwtxstatus *txstat;
dma_addr_t dmaaddr;
struct sk_buff *skb;
if (unlikely(!skb))
return -ENOMEM;
dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0);
- if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize)) {
+ if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) {
/* ugh. try to realloc in zone_dma */
gfp_flags |= GFP_DMA;
ring->rx_buffersize, 0);
}
- if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize)) {
+ if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) {
+ b43err(ring->dev->wl, "RX DMA buffer allocation failed\n");
dev_kfree_skb_any(skb);
return -EIO;
}
rxhdr = (struct b43_rxhdr_fw4 *)(skb->data);
rxhdr->frame_len = 0;
- txstat = (struct b43_hwtxstatus *)(skb->data);
- txstat->cookie = 0;
return 0;
}
return DMA_30BIT_MASK;
}
+static enum b43_dmatype dma_mask_to_engine_type(u64 dmamask)
+{
+ if (dmamask == DMA_30BIT_MASK)
+ return B43_DMA_30BIT;
+ if (dmamask == DMA_32BIT_MASK)
+ return B43_DMA_32BIT;
+ if (dmamask == DMA_64BIT_MASK)
+ return B43_DMA_64BIT;
+ B43_WARN_ON(1);
+ return B43_DMA_30BIT;
+}
+
/* Main initialization function. */
static
struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
goto err_kfree_meta;
/* test for ability to dma to txhdr_cache */
- dma_test = dma_map_single(dev->dev->dev,
+ dma_test = dma_map_single(dev->dev->dma_dev,
ring->txhdr_cache,
b43_txhdr_size(dev),
DMA_TO_DEVICE);
- if (b43_dma_mapping_error(ring, dma_test, b43_txhdr_size(dev))) {
+ if (b43_dma_mapping_error(ring, dma_test,
+ b43_txhdr_size(dev), 1)) {
/* ugh realloc */
kfree(ring->txhdr_cache);
ring->txhdr_cache = kcalloc(nr_slots,
if (!ring->txhdr_cache)
goto err_kfree_meta;
- dma_test = dma_map_single(dev->dev->dev,
+ dma_test = dma_map_single(dev->dev->dma_dev,
ring->txhdr_cache,
b43_txhdr_size(dev),
DMA_TO_DEVICE);
if (b43_dma_mapping_error(ring, dma_test,
- b43_txhdr_size(dev)))
+ b43_txhdr_size(dev), 1)) {
+
+ b43err(dev->wl,
+ "TXHDR DMA allocation failed\n");
goto err_kfree_txhdr_cache;
+ }
}
- dma_unmap_single(dev->dev->dev,
+ dma_unmap_single(dev->dev->dma_dev,
dma_test, b43_txhdr_size(dev),
DMA_TO_DEVICE);
}
goto out;
}
+#define divide(a, b) ({ \
+ typeof(a) __a = a; \
+ do_div(__a, b); \
+ __a; \
+ })
+
+#define modulo(a, b) ({ \
+ typeof(a) __a = a; \
+ do_div(__a, b); \
+ })
+
/* Main cleanup function. */
-static void b43_destroy_dmaring(struct b43_dmaring *ring)
+static void b43_destroy_dmaring(struct b43_dmaring *ring,
+ const char *ringname)
{
if (!ring)
return;
- b43dbg(ring->dev->wl, "DMA-%u 0x%04X (%s) max used slots: %d/%d\n",
- (unsigned int)(ring->type),
- ring->mmio_base,
- (ring->tx) ? "TX" : "RX", ring->max_used_slots, ring->nr_slots);
+#ifdef CONFIG_B43_DEBUG
+ {
+ /* Print some statistics. */
+ u64 failed_packets = ring->nr_failed_tx_packets;
+ u64 succeed_packets = ring->nr_succeed_tx_packets;
+ u64 nr_packets = failed_packets + succeed_packets;
+ u64 permille_failed = 0, average_tries = 0;
+
+ if (nr_packets)
+ permille_failed = divide(failed_packets * 1000, nr_packets);
+ if (nr_packets)
+ average_tries = divide(ring->nr_total_packet_tries * 100, nr_packets);
+
+ b43dbg(ring->dev->wl, "DMA-%u %s: "
+ "Used slots %d/%d, Failed frames %llu/%llu = %llu.%01llu%%, "
+ "Average tries %llu.%02llu\n",
+ (unsigned int)(ring->type), ringname,
+ ring->max_used_slots,
+ ring->nr_slots,
+ (unsigned long long)failed_packets,
+ (unsigned long long)nr_packets,
+ (unsigned long long)divide(permille_failed, 10),
+ (unsigned long long)modulo(permille_failed, 10),
+ (unsigned long long)divide(average_tries, 100),
+ (unsigned long long)modulo(average_tries, 100));
+ }
+#endif /* DEBUG */
+
/* Device IRQs are disabled prior entering this function,
* so no need to take care of concurrency with rx handler stuff.
*/
kfree(ring);
}
+#define destroy_ring(dma, ring) do { \
+ b43_destroy_dmaring((dma)->ring, __stringify(ring)); \
+ (dma)->ring = NULL; \
+ } while (0)
+
void b43_dma_free(struct b43_wldev *dev)
{
- struct b43_dma *dma = &dev->dma;
+ struct b43_dma *dma;
- b43_destroy_dmaring(dma->rx_ring3);
- dma->rx_ring3 = NULL;
- b43_destroy_dmaring(dma->rx_ring0);
- dma->rx_ring0 = NULL;
-
- b43_destroy_dmaring(dma->tx_ring5);
- dma->tx_ring5 = NULL;
- b43_destroy_dmaring(dma->tx_ring4);
- dma->tx_ring4 = NULL;
- b43_destroy_dmaring(dma->tx_ring3);
- dma->tx_ring3 = NULL;
- b43_destroy_dmaring(dma->tx_ring2);
- dma->tx_ring2 = NULL;
- b43_destroy_dmaring(dma->tx_ring1);
- dma->tx_ring1 = NULL;
- b43_destroy_dmaring(dma->tx_ring0);
- dma->tx_ring0 = NULL;
+ if (b43_using_pio_transfers(dev))
+ return;
+ dma = &dev->dma;
+
+ destroy_ring(dma, rx_ring);
+ destroy_ring(dma, tx_ring_AC_BK);
+ destroy_ring(dma, tx_ring_AC_BE);
+ destroy_ring(dma, tx_ring_AC_VI);
+ destroy_ring(dma, tx_ring_AC_VO);
+ destroy_ring(dma, tx_ring_mcast);
+}
+
+static int b43_dma_set_mask(struct b43_wldev *dev, u64 mask)
+{
+ u64 orig_mask = mask;
+ bool fallback = 0;
+ int err;
+
+ /* Try to set the DMA mask. If it fails, try falling back to a
+ * lower mask, as we can always also support a lower one. */
+ while (1) {
+ err = ssb_dma_set_mask(dev->dev, mask);
+ if (!err)
+ break;
+ if (mask == DMA_64BIT_MASK) {
+ mask = DMA_32BIT_MASK;
+ fallback = 1;
+ continue;
+ }
+ if (mask == DMA_32BIT_MASK) {
+ mask = DMA_30BIT_MASK;
+ fallback = 1;
+ continue;
+ }
+ b43err(dev->wl, "The machine/kernel does not support "
+ "the required %u-bit DMA mask\n",
+ (unsigned int)dma_mask_to_engine_type(orig_mask));
+ return -EOPNOTSUPP;
+ }
+ if (fallback) {
+ b43info(dev->wl, "DMA mask fallback from %u-bit to %u-bit\n",
+ (unsigned int)dma_mask_to_engine_type(orig_mask),
+ (unsigned int)dma_mask_to_engine_type(mask));
+ }
+
+ return 0;
}
int b43_dma_init(struct b43_wldev *dev)
{
struct b43_dma *dma = &dev->dma;
- struct b43_dmaring *ring;
int err;
u64 dmamask;
enum b43_dmatype type;
dmamask = supported_dma_mask(dev);
- switch (dmamask) {
- default:
- B43_WARN_ON(1);
- case DMA_30BIT_MASK:
- type = B43_DMA_30BIT;
- break;
- case DMA_32BIT_MASK:
- type = B43_DMA_32BIT;
- break;
- case DMA_64BIT_MASK:
- type = B43_DMA_64BIT;
- break;
- }
- err = ssb_dma_set_mask(dev->dev, dmamask);
- if (err) {
- b43err(dev->wl, "The machine/kernel does not support "
- "the required DMA mask (0x%08X%08X)\n",
- (unsigned int)((dmamask & 0xFFFFFFFF00000000ULL) >> 32),
- (unsigned int)(dmamask & 0x00000000FFFFFFFFULL));
- return -EOPNOTSUPP;
- }
+ type = dma_mask_to_engine_type(dmamask);
+ err = b43_dma_set_mask(dev, dmamask);
+ if (err)
+ return err;
err = -ENOMEM;
/* setup TX DMA channels. */
- ring = b43_setup_dmaring(dev, 0, 1, type);
- if (!ring)
+ dma->tx_ring_AC_BK = b43_setup_dmaring(dev, 0, 1, type);
+ if (!dma->tx_ring_AC_BK)
goto out;
- dma->tx_ring0 = ring;
- ring = b43_setup_dmaring(dev, 1, 1, type);
- if (!ring)
- goto err_destroy_tx0;
- dma->tx_ring1 = ring;
+ dma->tx_ring_AC_BE = b43_setup_dmaring(dev, 1, 1, type);
+ if (!dma->tx_ring_AC_BE)
+ goto err_destroy_bk;
- ring = b43_setup_dmaring(dev, 2, 1, type);
- if (!ring)
- goto err_destroy_tx1;
- dma->tx_ring2 = ring;
+ dma->tx_ring_AC_VI = b43_setup_dmaring(dev, 2, 1, type);
+ if (!dma->tx_ring_AC_VI)
+ goto err_destroy_be;
- ring = b43_setup_dmaring(dev, 3, 1, type);
- if (!ring)
- goto err_destroy_tx2;
- dma->tx_ring3 = ring;
+ dma->tx_ring_AC_VO = b43_setup_dmaring(dev, 3, 1, type);
+ if (!dma->tx_ring_AC_VO)
+ goto err_destroy_vi;
- ring = b43_setup_dmaring(dev, 4, 1, type);
- if (!ring)
- goto err_destroy_tx3;
- dma->tx_ring4 = ring;
+ dma->tx_ring_mcast = b43_setup_dmaring(dev, 4, 1, type);
+ if (!dma->tx_ring_mcast)
+ goto err_destroy_vo;
- ring = b43_setup_dmaring(dev, 5, 1, type);
- if (!ring)
- goto err_destroy_tx4;
- dma->tx_ring5 = ring;
+ /* setup RX DMA channel. */
+ dma->rx_ring = b43_setup_dmaring(dev, 0, 0, type);
+ if (!dma->rx_ring)
+ goto err_destroy_mcast;
- /* setup RX DMA channels. */
- ring = b43_setup_dmaring(dev, 0, 0, type);
- if (!ring)
- goto err_destroy_tx5;
- dma->rx_ring0 = ring;
-
- if (dev->dev->id.revision < 5) {
- ring = b43_setup_dmaring(dev, 3, 0, type);
- if (!ring)
- goto err_destroy_rx0;
- dma->rx_ring3 = ring;
- }
+ /* No support for the TX status DMA ring. */
+ B43_WARN_ON(dev->dev->id.revision < 5);
b43dbg(dev->wl, "%u-bit DMA initialized\n",
(unsigned int)type);
err = 0;
- out:
+out:
return err;
- err_destroy_rx0:
- b43_destroy_dmaring(dma->rx_ring0);
- dma->rx_ring0 = NULL;
- err_destroy_tx5:
- b43_destroy_dmaring(dma->tx_ring5);
- dma->tx_ring5 = NULL;
- err_destroy_tx4:
- b43_destroy_dmaring(dma->tx_ring4);
- dma->tx_ring4 = NULL;
- err_destroy_tx3:
- b43_destroy_dmaring(dma->tx_ring3);
- dma->tx_ring3 = NULL;
- err_destroy_tx2:
- b43_destroy_dmaring(dma->tx_ring2);
- dma->tx_ring2 = NULL;
- err_destroy_tx1:
- b43_destroy_dmaring(dma->tx_ring1);
- dma->tx_ring1 = NULL;
- err_destroy_tx0:
- b43_destroy_dmaring(dma->tx_ring0);
- dma->tx_ring0 = NULL;
- goto out;
+err_destroy_mcast:
+ destroy_ring(dma, tx_ring_mcast);
+err_destroy_vo:
+ destroy_ring(dma, tx_ring_AC_VO);
+err_destroy_vi:
+ destroy_ring(dma, tx_ring_AC_VI);
+err_destroy_be:
+ destroy_ring(dma, tx_ring_AC_BE);
+err_destroy_bk:
+ destroy_ring(dma, tx_ring_AC_BK);
+ return err;
}
/* Generate a cookie for the TX header. */
static u16 generate_cookie(struct b43_dmaring *ring, int slot)
{
- u16 cookie = 0x1000;
+ u16 cookie;
/* Use the upper 4 bits of the cookie as
* DMA controller ID and store the slot number
* It can also not be 0xFFFF because that is special
* for multicast frames.
*/
- switch (ring->index) {
- case 0:
- cookie = 0x1000;
- break;
- case 1:
- cookie = 0x2000;
- break;
- case 2:
- cookie = 0x3000;
- break;
- case 3:
- cookie = 0x4000;
- break;
- case 4:
- cookie = 0x5000;
- break;
- case 5:
- cookie = 0x6000;
- break;
- default:
- B43_WARN_ON(1);
- }
+ cookie = (((u16)ring->index + 1) << 12);
B43_WARN_ON(slot & ~0x0FFF);
- cookie |= (u16) slot;
+ cookie |= (u16)slot;
return cookie;
}
switch (cookie & 0xF000) {
case 0x1000:
- ring = dma->tx_ring0;
+ ring = dma->tx_ring_AC_BK;
break;
case 0x2000:
- ring = dma->tx_ring1;
+ ring = dma->tx_ring_AC_BE;
break;
case 0x3000:
- ring = dma->tx_ring2;
+ ring = dma->tx_ring_AC_VI;
break;
case 0x4000:
- ring = dma->tx_ring3;
+ ring = dma->tx_ring_AC_VO;
break;
case 0x5000:
- ring = dma->tx_ring4;
- break;
- case 0x6000:
- ring = dma->tx_ring5;
+ ring = dma->tx_ring_mcast;
break;
default:
B43_WARN_ON(1);
}
static int dma_tx_fragment(struct b43_dmaring *ring,
- struct sk_buff *skb,
- struct ieee80211_tx_control *ctl)
+ struct sk_buff *skb)
{
const struct b43_dma_ops *ops = ring->ops;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
u8 *header;
int slot, old_top_slot, old_used_slots;
int err;
size_t hdrsize = b43_txhdr_size(ring->dev);
#define SLOTS_PER_PACKET 2
- B43_WARN_ON(skb_shinfo(skb)->nr_frags);
old_top_slot = ring->current_slot;
old_used_slots = ring->used_slots;
header = &(ring->txhdr_cache[slot * hdrsize]);
cookie = generate_cookie(ring, slot);
err = b43_generate_txhdr(ring->dev, header,
- skb->data, skb->len, ctl, cookie);
+ skb->data, skb->len, info, cookie);
if (unlikely(err)) {
ring->current_slot = old_top_slot;
ring->used_slots = old_used_slots;
meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header,
hdrsize, 1);
- if (b43_dma_mapping_error(ring, meta_hdr->dmaaddr, hdrsize)) {
+ if (b43_dma_mapping_error(ring, meta_hdr->dmaaddr, hdrsize, 1)) {
ring->current_slot = old_top_slot;
ring->used_slots = old_used_slots;
return -EIO;
desc = ops->idx2desc(ring, slot, &meta);
memset(meta, 0, sizeof(*meta));
- memcpy(&meta->txstat.control, ctl, sizeof(*ctl));
meta->skb = skb;
meta->is_last_fragment = 1;
meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
/* create a bounce buffer in zone_dma on mapping failure. */
- if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len)) {
+ if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) {
bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
if (!bounce_skb) {
ring->current_slot = old_top_slot;
skb = bounce_skb;
meta->skb = skb;
meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
- if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len)) {
+ if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) {
ring->current_slot = old_top_slot;
ring->used_slots = old_used_slots;
err = -EIO;
ops->fill_descriptor(ring, desc, meta->dmaaddr, skb->len, 0, 1, 1);
- if (ctl->flags & IEEE80211_TXCTL_SEND_AFTER_DTIM) {
+ if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
/* Tell the firmware about the cookie of the last
* mcast frame, so it can clear the more-data bit in it. */
b43_shm_write16(ring->dev, B43_SHM_SHARED,
return 0;
}
-int b43_dma_tx(struct b43_wldev *dev,
- struct sk_buff *skb, struct ieee80211_tx_control *ctl)
+/* Static mapping of mac80211's queues (priorities) to b43 DMA rings. */
+static struct b43_dmaring * select_ring_by_priority(struct b43_wldev *dev,
+ u8 queue_prio)
+{
+ struct b43_dmaring *ring;
+
+ if (b43_modparam_qos) {
+ /* 0 = highest priority */
+ switch (queue_prio) {
+ default:
+ B43_WARN_ON(1);
+ /* fallthrough */
+ case 0:
+ ring = dev->dma.tx_ring_AC_VO;
+ break;
+ case 1:
+ ring = dev->dma.tx_ring_AC_VI;
+ break;
+ case 2:
+ ring = dev->dma.tx_ring_AC_BE;
+ break;
+ case 3:
+ ring = dev->dma.tx_ring_AC_BK;
+ break;
+ }
+ } else
+ ring = dev->dma.tx_ring_AC_BE;
+
+ return ring;
+}
+
+int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
{
struct b43_dmaring *ring;
struct ieee80211_hdr *hdr;
int err = 0;
unsigned long flags;
-
- if (unlikely(skb->len < 2 + 2 + 6)) {
- /* Too short, this can't be a valid frame. */
- return -EINVAL;
- }
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
hdr = (struct ieee80211_hdr *)skb->data;
- if (ctl->flags & IEEE80211_TXCTL_SEND_AFTER_DTIM) {
+ if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
/* The multicast ring will be sent after the DTIM */
- ring = dev->dma.tx_ring4;
+ ring = dev->dma.tx_ring_mcast;
/* Set the more-data bit. Ucode will clear it on
* the last frame for us. */
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
} else {
/* Decide by priority where to put this frame. */
- ring = priority_to_txring(dev, ctl->queue);
+ ring = select_ring_by_priority(
+ dev, skb_get_queue_mapping(skb));
}
spin_lock_irqsave(&ring->lock, flags);
* That would be a mac80211 bug. */
B43_WARN_ON(ring->stopped);
- err = dma_tx_fragment(ring, skb, ctl);
+ /* Assign the queue number to the ring (if not already done before)
+ * so TX status handling can use it. The queue to ring mapping is
+ * static, so we don't need to store it per frame. */
+ ring->queue_prio = skb_get_queue_mapping(skb);
+
+ err = dma_tx_fragment(ring, skb);
if (unlikely(err == -ENOKEY)) {
/* Drop this packet, as we don't have the encryption key
* anymore and must not transmit it unencrypted. */
if ((free_slots(ring) < SLOTS_PER_PACKET) ||
should_inject_overflow(ring)) {
/* This TX ring is full. */
- ieee80211_stop_queue(dev->wl->hw, txring_to_priority(ring));
+ ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
ring->stopped = 1;
if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index);
return err;
}
+/* Called with IRQs disabled. */
void b43_dma_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status)
{
struct b43_dmadesc_generic *desc;
struct b43_dmadesc_meta *meta;
int slot;
+ bool frame_succeed;
ring = parse_cookie(dev, status->cookie, &slot);
if (unlikely(!ring))
return;
- B43_WARN_ON(!irqs_disabled());
- spin_lock(&ring->lock);
+
+ spin_lock(&ring->lock); /* IRQs are already disabled. */
B43_WARN_ON(!ring->tx);
ops = ring->ops;
b43_txhdr_size(dev), 1);
if (meta->is_last_fragment) {
- B43_WARN_ON(!meta->skb);
- /* Call back to inform the ieee80211 subsystem about the
- * status of the transmission.
- * Some fields of txstat are already filled in dma_tx().
+ struct ieee80211_tx_info *info;
+
+ BUG_ON(!meta->skb);
+
+ info = IEEE80211_SKB_CB(meta->skb);
+
+ memset(&info->status, 0, sizeof(info->status));
+
+ /*
+ * Call back to inform the ieee80211 subsystem about
+ * the status of the transmission.
*/
- if (status->acked) {
- meta->txstat.flags |= IEEE80211_TX_STATUS_ACK;
- } else {
- if (!(meta->txstat.control.flags
- & IEEE80211_TXCTL_NO_ACK))
- meta->txstat.excessive_retries = 1;
- }
- if (status->frame_count == 0) {
- /* The frame was not transmitted at all. */
- meta->txstat.retry_count = 0;
- } else
- meta->txstat.retry_count = status->frame_count - 1;
- ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb,
- &(meta->txstat));
+ frame_succeed = b43_fill_txstatus_report(info, status);
+#ifdef CONFIG_B43_DEBUG
+ if (frame_succeed)
+ ring->nr_succeed_tx_packets++;
+ else
+ ring->nr_failed_tx_packets++;
+ ring->nr_total_packet_tries += status->frame_count;
+#endif /* DEBUG */
+ ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb);
+
/* skb is freed by ieee80211_tx_status_irqsafe() */
meta->skb = NULL;
} else {
dev->stats.last_tx = jiffies;
if (ring->stopped) {
B43_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET);
- ieee80211_wake_queue(dev->wl->hw, txring_to_priority(ring));
+ ieee80211_wake_queue(dev->wl->hw, ring->queue_prio);
ring->stopped = 0;
if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
b43dbg(dev->wl, "Woke up TX ring %d\n", ring->index);
{
const int nr_queues = dev->wl->hw->queues;
struct b43_dmaring *ring;
- struct ieee80211_tx_queue_stats_data *data;
unsigned long flags;
int i;
for (i = 0; i < nr_queues; i++) {
- data = &(stats->data[i]);
- ring = priority_to_txring(dev, i);
+ ring = select_ring_by_priority(dev, i);
spin_lock_irqsave(&ring->lock, flags);
- data->len = ring->used_slots / SLOTS_PER_PACKET;
- data->limit = ring->nr_slots / SLOTS_PER_PACKET;
- data->count = ring->nr_tx_packets;
+ stats[i].len = ring->used_slots / SLOTS_PER_PACKET;
+ stats[i].limit = ring->nr_slots / SLOTS_PER_PACKET;
+ stats[i].count = ring->nr_tx_packets;
spin_unlock_irqrestore(&ring->lock, flags);
}
}
sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize);
skb = meta->skb;
- if (ring->index == 3) {
- /* We received an xmit status. */
- struct b43_hwtxstatus *hw = (struct b43_hwtxstatus *)skb->data;
- int i = 0;
-
- while (hw->cookie == 0) {
- if (i > 100)
- break;
- i++;
- udelay(2);
- barrier();
- }
- b43_handle_hwtxstatus(ring->dev, hw);
- /* recycle the descriptor buffer. */
- sync_descbuffer_for_device(ring, meta->dmaaddr,
- ring->rx_buffersize);
-
- return;
- }
rxhdr = (struct b43_rxhdr_fw4 *)skb->data;
len = le16_to_cpu(rxhdr->frame_len);
if (len == 0) {
skb_pull(skb, ring->frameoffset);
b43_rx(ring->dev, skb, rxhdr);
- drop:
+drop:
return;
}
void b43_dma_tx_suspend(struct b43_wldev *dev)
{
b43_power_saving_ctl_bits(dev, B43_PS_AWAKE);
- b43_dma_tx_suspend_ring(dev->dma.tx_ring0);
- b43_dma_tx_suspend_ring(dev->dma.tx_ring1);
- b43_dma_tx_suspend_ring(dev->dma.tx_ring2);
- b43_dma_tx_suspend_ring(dev->dma.tx_ring3);
- b43_dma_tx_suspend_ring(dev->dma.tx_ring4);
- b43_dma_tx_suspend_ring(dev->dma.tx_ring5);
+ b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BK);
+ b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BE);
+ b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VI);
+ b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VO);
+ b43_dma_tx_suspend_ring(dev->dma.tx_ring_mcast);
}
void b43_dma_tx_resume(struct b43_wldev *dev)
{
- b43_dma_tx_resume_ring(dev->dma.tx_ring5);
- b43_dma_tx_resume_ring(dev->dma.tx_ring4);
- b43_dma_tx_resume_ring(dev->dma.tx_ring3);
- b43_dma_tx_resume_ring(dev->dma.tx_ring2);
- b43_dma_tx_resume_ring(dev->dma.tx_ring1);
- b43_dma_tx_resume_ring(dev->dma.tx_ring0);
+ b43_dma_tx_resume_ring(dev->dma.tx_ring_mcast);
+ b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VO);
+ b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VI);
+ b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BE);
+ b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BK);
b43_power_saving_ctl_bits(dev, 0);
}
+
+#ifdef CONFIG_B43_PIO
+static void direct_fifo_rx(struct b43_wldev *dev, enum b43_dmatype type,
+ u16 mmio_base, bool enable)
+{
+ u32 ctl;
+
+ if (type == B43_DMA_64BIT) {
+ ctl = b43_read32(dev, mmio_base + B43_DMA64_RXCTL);
+ ctl &= ~B43_DMA64_RXDIRECTFIFO;
+ if (enable)
+ ctl |= B43_DMA64_RXDIRECTFIFO;
+ b43_write32(dev, mmio_base + B43_DMA64_RXCTL, ctl);
+ } else {
+ ctl = b43_read32(dev, mmio_base + B43_DMA32_RXCTL);
+ ctl &= ~B43_DMA32_RXDIRECTFIFO;
+ if (enable)
+ ctl |= B43_DMA32_RXDIRECTFIFO;
+ b43_write32(dev, mmio_base + B43_DMA32_RXCTL, ctl);
+ }
+}
+
+/* Enable/Disable Direct FIFO Receive Mode (PIO) on a RX engine.
+ * This is called from PIO code, so DMA structures are not available. */
+void b43_dma_direct_fifo_rx(struct b43_wldev *dev,
+ unsigned int engine_index, bool enable)
+{
+ enum b43_dmatype type;
+ u16 mmio_base;
+
+ type = dma_mask_to_engine_type(supported_dma_mask(dev));
+
+ mmio_base = b43_dmacontroller_base(type, engine_index);
+ direct_fifo_rx(dev, type, mmio_base, enable);
+}
+#endif /* CONFIG_B43_PIO */
dma_addr_t dmaaddr;
/* ieee80211 TX status. Only used once per 802.11 frag. */
bool is_last_fragment;
- struct ieee80211_tx_status txstat;
};
struct b43_dmaring;
enum b43_dmatype type;
/* Boolean. Is this ring stopped at ieee80211 level? */
bool stopped;
+ /* The QOS priority assigned to this ring. Only used for TX rings.
+ * This is the mac80211 "queue" value. */
+ u8 queue_prio;
/* Lock, only used for TX. */
spinlock_t lock;
struct b43_wldev *dev;
int max_used_slots;
/* Last time we injected a ring overflow. */
unsigned long last_injected_overflow;
-#endif /* CONFIG_B43_DEBUG */
+ /* Statistics: Number of successfully transmitted packets */
+ u64 nr_succeed_tx_packets;
+ /* Statistics: Number of failed TX packets */
+ u64 nr_failed_tx_packets;
+ /* Statistics: Total number of TX plus all retries. */
+ u64 nr_total_packet_tries;
+#endif /* CONFIG_B43_DEBUG */
};
static inline u32 b43_dma_read(struct b43_dmaring *ring, u16 offset)
struct ieee80211_tx_queue_stats *stats);
int b43_dma_tx(struct b43_wldev *dev,
- struct sk_buff *skb, struct ieee80211_tx_control *ctl);
+ struct sk_buff *skb);
void b43_dma_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status);
void b43_dma_rx(struct b43_dmaring *ring);
+void b43_dma_direct_fifo_rx(struct b43_wldev *dev,
+ unsigned int engine_index, bool enable);
+
#endif /* B43_DMA_H_ */
case B43_LED_TRANSFER:
case B43_LED_APTRANSFER:
snprintf(name, sizeof(name),
- "b43-%s:tx", wiphy_name(hw->wiphy));
+ "b43-%s::tx", wiphy_name(hw->wiphy));
b43_register_led(dev, &dev->led_tx, name,
ieee80211_get_tx_led_name(hw),
led_index, activelow);
snprintf(name, sizeof(name),
- "b43-%s:rx", wiphy_name(hw->wiphy));
+ "b43-%s::rx", wiphy_name(hw->wiphy));
b43_register_led(dev, &dev->led_rx, name,
ieee80211_get_rx_led_name(hw),
led_index, activelow);
case B43_LED_RADIO_B:
case B43_LED_MODE_BG:
snprintf(name, sizeof(name),
- "b43-%s:radio", wiphy_name(hw->wiphy));
+ "b43-%s::radio", wiphy_name(hw->wiphy));
b43_register_led(dev, &dev->led_radio, name,
b43_rfkill_led_name(dev),
led_index, activelow);
case B43_LED_WEIRD:
case B43_LED_ASSOC:
snprintf(name, sizeof(name),
- "b43-%s:assoc", wiphy_name(hw->wiphy));
+ "b43-%s::assoc", wiphy_name(hw->wiphy));
b43_register_led(dev, &dev->led_assoc, name,
ieee80211_get_assoc_led_name(hw),
led_index, activelow);
#include <linux/sched.h>
-/* Define to 1 to always calibrate all possible LO control pairs.
- * This is a workaround until we fix the partial LO calibration optimization. */
-#define B43_CALIB_ALL_LOCTLS 1
+static struct b43_lo_calib * b43_find_lo_calib(struct b43_txpower_lo_control *lo,
+ const struct b43_bbatt *bbatt,
+ const struct b43_rfatt *rfatt)
+{
+ struct b43_lo_calib *c;
+
+ list_for_each_entry(c, &lo->calib_list, list) {
+ if (!b43_compare_bbatt(&c->bbatt, bbatt))
+ continue;
+ if (!b43_compare_rfatt(&c->rfatt, rfatt))
+ continue;
+ return c;
+ }
+ return NULL;
+}
/* Write the LocalOscillator Control (adjust) value-pair. */
static void b43_lo_write(struct b43_wldev *dev, struct b43_loctl *control)
{
struct b43_phy *phy = &dev->phy;
u16 value;
- u16 reg;
if (B43_DEBUG) {
if (unlikely(abs(control->i) > 16 || abs(control->q) > 16)) {
return;
}
}
+ B43_WARN_ON(phy->type != B43_PHYTYPE_G);
value = (u8) (control->q);
value |= ((u8) (control->i)) << 8;
-
- reg = (phy->type == B43_PHYTYPE_B) ? 0x002F : B43_PHY_LO_CTL;
- b43_phy_write(dev, reg, value);
-}
-
-static int assert_rfatt_and_bbatt(const struct b43_rfatt *rfatt,
- const struct b43_bbatt *bbatt,
- struct b43_wldev *dev)
-{
- int err = 0;
-
- /* Check the attenuation values against the LO control array sizes. */
- if (unlikely(rfatt->att >= B43_NR_RF)) {
- b43err(dev->wl, "rfatt(%u) >= size of LO array\n", rfatt->att);
- err = -EINVAL;
- }
- if (unlikely(bbatt->att >= B43_NR_BB)) {
- b43err(dev->wl, "bbatt(%u) >= size of LO array\n", bbatt->att);
- err = -EINVAL;
- }
-
- return err;
-}
-
-#if !B43_CALIB_ALL_LOCTLS
-static
-struct b43_loctl *b43_get_lo_g_ctl_nopadmix(struct b43_wldev *dev,
- const struct b43_rfatt *rfatt,
- const struct b43_bbatt *bbatt)
-{
- struct b43_phy *phy = &dev->phy;
- struct b43_txpower_lo_control *lo = phy->lo_control;
-
- if (assert_rfatt_and_bbatt(rfatt, bbatt, dev))
- return &(lo->no_padmix[0][0]); /* Just prevent a crash */
- return &(lo->no_padmix[bbatt->att][rfatt->att]);
-}
-#endif /* !B43_CALIB_ALL_LOCTLS */
-
-struct b43_loctl *b43_get_lo_g_ctl(struct b43_wldev *dev,
- const struct b43_rfatt *rfatt,
- const struct b43_bbatt *bbatt)
-{
- struct b43_phy *phy = &dev->phy;
- struct b43_txpower_lo_control *lo = phy->lo_control;
-
- if (assert_rfatt_and_bbatt(rfatt, bbatt, dev))
- return &(lo->no_padmix[0][0]); /* Just prevent a crash */
- if (rfatt->with_padmix)
- return &(lo->with_padmix[bbatt->att][rfatt->att]);
- return &(lo->no_padmix[bbatt->att][rfatt->att]);
-}
-
-/* Call a function for every possible LO control value-pair. */
-static void b43_call_for_each_loctl(struct b43_wldev *dev,
- void (*func) (struct b43_wldev *,
- struct b43_loctl *))
-{
- struct b43_phy *phy = &dev->phy;
- struct b43_txpower_lo_control *ctl = phy->lo_control;
- int i, j;
-
- for (i = 0; i < B43_NR_BB; i++) {
- for (j = 0; j < B43_NR_RF; j++)
- func(dev, &(ctl->with_padmix[i][j]));
- }
- for (i = 0; i < B43_NR_BB; i++) {
- for (j = 0; j < B43_NR_RF; j++)
- func(dev, &(ctl->no_padmix[i][j]));
- }
-}
-
-static u16 lo_b_r15_loop(struct b43_wldev *dev)
-{
- int i;
- u16 ret = 0;
-
- for (i = 0; i < 10; i++) {
- b43_phy_write(dev, 0x0015, 0xAFA0);
- udelay(1);
- b43_phy_write(dev, 0x0015, 0xEFA0);
- udelay(10);
- b43_phy_write(dev, 0x0015, 0xFFA0);
- udelay(40);
- ret += b43_phy_read(dev, 0x002C);
- }
-
- return ret;
-}
-
-void b43_lo_b_measure(struct b43_wldev *dev)
-{
- struct b43_phy *phy = &dev->phy;
- u16 regstack[12] = { 0 };
- u16 mls;
- u16 fval;
- int i, j;
-
- regstack[0] = b43_phy_read(dev, 0x0015);
- regstack[1] = b43_radio_read16(dev, 0x0052) & 0xFFF0;
-
- if (phy->radio_ver == 0x2053) {
- regstack[2] = b43_phy_read(dev, 0x000A);
- regstack[3] = b43_phy_read(dev, 0x002A);
- regstack[4] = b43_phy_read(dev, 0x0035);
- regstack[5] = b43_phy_read(dev, 0x0003);
- regstack[6] = b43_phy_read(dev, 0x0001);
- regstack[7] = b43_phy_read(dev, 0x0030);
-
- regstack[8] = b43_radio_read16(dev, 0x0043);
- regstack[9] = b43_radio_read16(dev, 0x007A);
- regstack[10] = b43_read16(dev, 0x03EC);
- regstack[11] = b43_radio_read16(dev, 0x0052) & 0x00F0;
-
- b43_phy_write(dev, 0x0030, 0x00FF);
- b43_write16(dev, 0x03EC, 0x3F3F);
- b43_phy_write(dev, 0x0035, regstack[4] & 0xFF7F);
- b43_radio_write16(dev, 0x007A, regstack[9] & 0xFFF0);
- }
- b43_phy_write(dev, 0x0015, 0xB000);
- b43_phy_write(dev, 0x002B, 0x0004);
-
- if (phy->radio_ver == 0x2053) {
- b43_phy_write(dev, 0x002B, 0x0203);
- b43_phy_write(dev, 0x002A, 0x08A3);
- }
-
- phy->minlowsig[0] = 0xFFFF;
-
- for (i = 0; i < 4; i++) {
- b43_radio_write16(dev, 0x0052, regstack[1] | i);
- lo_b_r15_loop(dev);
- }
- for (i = 0; i < 10; i++) {
- b43_radio_write16(dev, 0x0052, regstack[1] | i);
- mls = lo_b_r15_loop(dev) / 10;
- if (mls < phy->minlowsig[0]) {
- phy->minlowsig[0] = mls;
- phy->minlowsigpos[0] = i;
- }
- }
- b43_radio_write16(dev, 0x0052, regstack[1] | phy->minlowsigpos[0]);
-
- phy->minlowsig[1] = 0xFFFF;
-
- for (i = -4; i < 5; i += 2) {
- for (j = -4; j < 5; j += 2) {
- if (j < 0)
- fval = (0x0100 * i) + j + 0x0100;
- else
- fval = (0x0100 * i) + j;
- b43_phy_write(dev, 0x002F, fval);
- mls = lo_b_r15_loop(dev) / 10;
- if (mls < phy->minlowsig[1]) {
- phy->minlowsig[1] = mls;
- phy->minlowsigpos[1] = fval;
- }
- }
- }
- phy->minlowsigpos[1] += 0x0101;
-
- b43_phy_write(dev, 0x002F, phy->minlowsigpos[1]);
- if (phy->radio_ver == 0x2053) {
- b43_phy_write(dev, 0x000A, regstack[2]);
- b43_phy_write(dev, 0x002A, regstack[3]);
- b43_phy_write(dev, 0x0035, regstack[4]);
- b43_phy_write(dev, 0x0003, regstack[5]);
- b43_phy_write(dev, 0x0001, regstack[6]);
- b43_phy_write(dev, 0x0030, regstack[7]);
-
- b43_radio_write16(dev, 0x0043, regstack[8]);
- b43_radio_write16(dev, 0x007A, regstack[9]);
-
- b43_radio_write16(dev, 0x0052,
- (b43_radio_read16(dev, 0x0052) & 0x000F)
- | regstack[11]);
-
- b43_write16(dev, 0x03EC, regstack[10]);
- }
- b43_phy_write(dev, 0x0015, regstack[0]);
+ b43_phy_write(dev, B43_PHY_LO_CTL, value);
}
static u16 lo_measure_feedthrough(struct b43_wldev *dev,
if (lb_gain > 10) {
radio_pctl_reg = 0;
pga = abs(10 - lb_gain) / 6;
- pga = limit_value(pga, 0, 15);
+ pga = clamp_val(pga, 0, 15);
} else {
int cmp_val;
int tmp;
b43_radio_write16(dev, 0x52, b43_radio_read16(dev, 0x52)
& 0xFFF0); /* TX bias == 0 */
}
+ lo->txctl_measured_time = jiffies;
}
static void lo_read_power_vector(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
struct b43_txpower_lo_control *lo = phy->lo_control;
- u16 i;
+ int i;
u64 tmp;
u64 power_vector = 0;
- int rf_offset, bb_offset;
- struct b43_loctl *loctl;
for (i = 0; i < 8; i += 2) {
tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x310 + i);
- /* Clear the top byte. We get holes in the bitmap... */
- tmp &= 0xFF;
power_vector |= (tmp << (i * 8));
/* Clear the vector on the device. */
b43_shm_write16(dev, B43_SHM_SHARED, 0x310 + i, 0);
}
-
if (power_vector)
lo->power_vector = power_vector;
- power_vector = lo->power_vector;
-
- for (i = 0; i < 64; i++) {
- if (power_vector & ((u64) 1ULL << i)) {
- /* Now figure out which b43_loctl corresponds
- * to this bit.
- */
- rf_offset = i / lo->rfatt_list.len;
- bb_offset = i % lo->rfatt_list.len; //FIXME?
- loctl =
- b43_get_lo_g_ctl(dev,
- &lo->rfatt_list.list[rf_offset],
- &lo->bbatt_list.list[bb_offset]);
- /* And mark it as "used", as the device told us
- * through the bitmap it is using it.
- */
- loctl->used = 1;
- }
- }
+ lo->pwr_vec_read_time = jiffies;
}
/* 802.11/LO/GPHY/MeasuringGains */
phy->lna_lod_gain = 1;
trsw_rx_gain -= 8;
}
- trsw_rx_gain = limit_value(trsw_rx_gain, 0, 0x2D);
+ trsw_rx_gain = clamp_val(trsw_rx_gain, 0, 0x2D);
phy->pga_gain = trsw_rx_gain / 3;
if (phy->pga_gain >= 5) {
phy->pga_gain -= 5;
b43_phy_write(dev, B43_PHY_CCK(0x16), 0x410);
b43_phy_write(dev, B43_PHY_CCK(0x17), 0x820);
}
- if (!lo->rebuild && b43_has_hardware_pctl(phy))
- lo_read_power_vector(dev);
if (phy->rev >= 2) {
sav->phy_analogover = b43_phy_read(dev, B43_PHY_ANALOGOVER);
sav->phy_analogoverval =
b43_radio_read16(dev, 0x51); /* dummy read */
if (phy->type == B43_PHYTYPE_G)
b43_phy_write(dev, B43_PHY_CCK(0x2F), 0);
- if (lo->rebuild)
+
+ /* Re-measure the txctl values, if needed. */
+ if (time_before(lo->txctl_measured_time,
+ jiffies - B43_LO_TXCTL_EXPIRE))
lo_measure_txctl_values(dev);
+
if (phy->type == B43_PHYTYPE_G && phy->rev >= 3) {
b43_phy_write(dev, B43_PHY_LO_MASK, 0xC078);
} else {
struct lo_g_saved_values *sav)
{
struct b43_phy *phy = &dev->phy;
- struct b43_txpower_lo_control *lo = phy->lo_control;
u16 tmp;
if (phy->rev >= 2) {
tmp = (phy->pga_gain | 0xEFA0);
b43_phy_write(dev, B43_PHY_PGACTL, tmp);
}
- if (b43_has_hardware_pctl(phy)) {
- b43_gphy_dc_lt_init(dev);
- } else {
- if (lo->rebuild)
- b43_lo_g_adjust_to(dev, 3, 2, 0);
- else
- b43_lo_g_adjust(dev);
- }
if (phy->type == B43_PHYTYPE_G) {
if (phy->rev >= 3)
b43_phy_write(dev, B43_PHY_CCK(0x2E), 0xC078);
struct b43_lo_g_statemachine *d)
{
struct b43_phy *phy = &dev->phy;
- struct b43_txpower_lo_control *lo = phy->lo_control;
struct b43_loctl test_loctl;
struct b43_loctl orig_loctl;
struct b43_loctl prev_loctl = {
found_lower = 1;
d->lowest_feedth = feedth;
if ((d->nr_measured < 2) &&
- (!has_loopback_gain(phy) || lo->rebuild))
+ !has_loopback_gain(phy))
break;
}
}
int *max_rx_gain)
{
struct b43_phy *phy = &dev->phy;
- struct b43_txpower_lo_control *lo = phy->lo_control;
struct b43_lo_g_statemachine d;
u16 feedth;
int found_lower;
d.nr_measured = 0;
d.state_val_multiplier = 1;
- if (has_loopback_gain(phy) && !lo->rebuild)
+ if (has_loopback_gain(phy))
d.state_val_multiplier = 3;
memcpy(&d.min_loctl, loctl, sizeof(struct b43_loctl));
- if (has_loopback_gain(phy) && lo->rebuild)
+ if (has_loopback_gain(phy))
max_repeat = 4;
do {
b43_lo_write(dev, &d.min_loctl);
feedth = lo_measure_feedthrough(dev, phy->lna_gain,
phy->pga_gain,
phy->trsw_rx_gain);
- if (!lo->rebuild && feedth < 0x258) {
+ if (feedth < 0x258) {
if (feedth >= 0x12C)
*max_rx_gain += 6;
else
} while (++repeat_cnt < max_repeat);
}
-#if B43_CALIB_ALL_LOCTLS
-static const struct b43_rfatt b43_full_rfatt_list_items[] = {
- { .att = 0, .with_padmix = 0, },
- { .att = 1, .with_padmix = 0, },
- { .att = 2, .with_padmix = 0, },
- { .att = 3, .with_padmix = 0, },
- { .att = 4, .with_padmix = 0, },
- { .att = 5, .with_padmix = 0, },
- { .att = 6, .with_padmix = 0, },
- { .att = 7, .with_padmix = 0, },
- { .att = 8, .with_padmix = 0, },
- { .att = 9, .with_padmix = 0, },
- { .att = 10, .with_padmix = 0, },
- { .att = 11, .with_padmix = 0, },
- { .att = 12, .with_padmix = 0, },
- { .att = 13, .with_padmix = 0, },
- { .att = 14, .with_padmix = 0, },
- { .att = 15, .with_padmix = 0, },
- { .att = 0, .with_padmix = 1, },
- { .att = 1, .with_padmix = 1, },
- { .att = 2, .with_padmix = 1, },
- { .att = 3, .with_padmix = 1, },
- { .att = 4, .with_padmix = 1, },
- { .att = 5, .with_padmix = 1, },
- { .att = 6, .with_padmix = 1, },
- { .att = 7, .with_padmix = 1, },
- { .att = 8, .with_padmix = 1, },
- { .att = 9, .with_padmix = 1, },
- { .att = 10, .with_padmix = 1, },
- { .att = 11, .with_padmix = 1, },
- { .att = 12, .with_padmix = 1, },
- { .att = 13, .with_padmix = 1, },
- { .att = 14, .with_padmix = 1, },
- { .att = 15, .with_padmix = 1, },
-};
-static const struct b43_rfatt_list b43_full_rfatt_list = {
- .list = b43_full_rfatt_list_items,
- .len = ARRAY_SIZE(b43_full_rfatt_list_items),
-};
-
-static const struct b43_bbatt b43_full_bbatt_list_items[] = {
- { .att = 0, },
- { .att = 1, },
- { .att = 2, },
- { .att = 3, },
- { .att = 4, },
- { .att = 5, },
- { .att = 6, },
- { .att = 7, },
- { .att = 8, },
- { .att = 9, },
- { .att = 10, },
- { .att = 11, },
-};
-static const struct b43_bbatt_list b43_full_bbatt_list = {
- .list = b43_full_bbatt_list_items,
- .len = ARRAY_SIZE(b43_full_bbatt_list_items),
-};
-#endif /* B43_CALIB_ALL_LOCTLS */
-
-static void lo_measure(struct b43_wldev *dev)
+static
+struct b43_lo_calib * b43_calibrate_lo_setting(struct b43_wldev *dev,
+ const struct b43_bbatt *bbatt,
+ const struct b43_rfatt *rfatt)
{
struct b43_phy *phy = &dev->phy;
- struct b43_txpower_lo_control *lo = phy->lo_control;
struct b43_loctl loctl = {
.i = 0,
.q = 0,
};
- struct b43_loctl *ploctl;
int max_rx_gain;
- int rfidx, bbidx;
- const struct b43_bbatt_list *bbatt_list;
- const struct b43_rfatt_list *rfatt_list;
-
+ struct b43_lo_calib *cal;
+ struct lo_g_saved_values uninitialized_var(saved_regs);
/* Values from the "TXCTL Register and Value Table" */
u16 txctl_reg;
u16 txctl_value;
u16 pad_mix_gain;
- bbatt_list = &lo->bbatt_list;
- rfatt_list = &lo->rfatt_list;
-#if B43_CALIB_ALL_LOCTLS
- bbatt_list = &b43_full_bbatt_list;
- rfatt_list = &b43_full_rfatt_list;
-#endif
+ saved_regs.old_channel = phy->channel;
+ b43_mac_suspend(dev);
+ lo_measure_setup(dev, &saved_regs);
txctl_reg = lo_txctl_register_table(dev, &txctl_value, &pad_mix_gain);
- for (rfidx = 0; rfidx < rfatt_list->len; rfidx++) {
-
- b43_radio_write16(dev, 0x43, (b43_radio_read16(dev, 0x43)
- & 0xFFF0) |
- rfatt_list->list[rfidx].att);
- b43_radio_write16(dev, txctl_reg,
- (b43_radio_read16(dev, txctl_reg)
- & ~txctl_value)
- | (rfatt_list->list[rfidx].with_padmix ?
- txctl_value : 0));
-
- for (bbidx = 0; bbidx < bbatt_list->len; bbidx++) {
- if (lo->rebuild) {
-#if B43_CALIB_ALL_LOCTLS
- ploctl = b43_get_lo_g_ctl(dev,
- &rfatt_list->list[rfidx],
- &bbatt_list->list[bbidx]);
-#else
- ploctl = b43_get_lo_g_ctl_nopadmix(dev,
- &rfatt_list->
- list[rfidx],
- &bbatt_list->
- list[bbidx]);
-#endif
- } else {
- ploctl = b43_get_lo_g_ctl(dev,
- &rfatt_list->list[rfidx],
- &bbatt_list->list[bbidx]);
- if (!ploctl->used)
- continue;
- }
- memcpy(&loctl, ploctl, sizeof(loctl));
- loctl.i = 0;
- loctl.q = 0;
-
- max_rx_gain = rfatt_list->list[rfidx].att * 2;
- max_rx_gain += bbatt_list->list[bbidx].att / 2;
- if (rfatt_list->list[rfidx].with_padmix)
- max_rx_gain -= pad_mix_gain;
- if (has_loopback_gain(phy))
- max_rx_gain += phy->max_lb_gain;
- lo_measure_gain_values(dev, max_rx_gain,
- has_loopback_gain(phy));
-
- b43_phy_set_baseband_attenuation(dev,
- bbatt_list->list[bbidx].att);
- lo_probe_loctls_statemachine(dev, &loctl, &max_rx_gain);
- if (phy->type == B43_PHYTYPE_B) {
- loctl.i++;
- loctl.q++;
- }
- b43_loctl_set_calibrated(&loctl, 1);
- memcpy(ploctl, &loctl, sizeof(loctl));
- }
- }
-}
-
-#if B43_DEBUG
-static void do_validate_loctl(struct b43_wldev *dev, struct b43_loctl *control)
-{
- const int is_initializing = (b43_status(dev) == B43_STAT_UNINIT);
- int i = control->i;
- int q = control->q;
+ b43_radio_write16(dev, 0x43,
+ (b43_radio_read16(dev, 0x43) & 0xFFF0)
+ | rfatt->att);
+ b43_radio_write16(dev, txctl_reg,
+ (b43_radio_read16(dev, txctl_reg) & ~txctl_value)
+ | (rfatt->with_padmix) ? txctl_value : 0);
- if (b43_loctl_is_calibrated(control)) {
- if ((abs(i) > 16) || (abs(q) > 16))
- goto error;
- } else {
- if (control->used)
- goto error;
- if (dev->phy.lo_control->rebuild) {
- control->i = 0;
- control->q = 0;
- if ((i != B43_LOCTL_POISON) ||
- (q != B43_LOCTL_POISON))
- goto error;
- }
+ max_rx_gain = rfatt->att * 2;
+ max_rx_gain += bbatt->att / 2;
+ if (rfatt->with_padmix)
+ max_rx_gain -= pad_mix_gain;
+ if (has_loopback_gain(phy))
+ max_rx_gain += phy->max_lb_gain;
+ lo_measure_gain_values(dev, max_rx_gain,
+ has_loopback_gain(phy));
+
+ b43_phy_set_baseband_attenuation(dev, bbatt->att);
+ lo_probe_loctls_statemachine(dev, &loctl, &max_rx_gain);
+
+ lo_measure_restore(dev, &saved_regs);
+ b43_mac_enable(dev);
+
+ if (b43_debug(dev, B43_DBG_LO)) {
+ b43dbg(dev->wl, "LO: Calibrated for BB(%u), RF(%u,%u) "
+ "=> I=%d Q=%d\n",
+ bbatt->att, rfatt->att, rfatt->with_padmix,
+ loctl.i, loctl.q);
}
- if (is_initializing && control->used)
- goto error;
-
- return;
-error:
- b43err(dev->wl, "LO control pair validation failed "
- "(I: %d, Q: %d, used %u, calib: %u, initing: %d)\n",
- i, q, control->used,
- b43_loctl_is_calibrated(control),
- is_initializing);
-}
-static void validate_all_loctls(struct b43_wldev *dev)
-{
- b43_call_for_each_loctl(dev, do_validate_loctl);
-}
-
-static void do_reset_calib(struct b43_wldev *dev, struct b43_loctl *control)
-{
- if (dev->phy.lo_control->rebuild ||
- control->used) {
- b43_loctl_set_calibrated(control, 0);
- control->i = B43_LOCTL_POISON;
- control->q = B43_LOCTL_POISON;
+ cal = kmalloc(sizeof(*cal), GFP_KERNEL);
+ if (!cal) {
+ b43warn(dev->wl, "LO calib: out of memory\n");
+ return NULL;
}
+ memcpy(&cal->bbatt, bbatt, sizeof(*bbatt));
+ memcpy(&cal->rfatt, rfatt, sizeof(*rfatt));
+ memcpy(&cal->ctl, &loctl, sizeof(loctl));
+ cal->calib_time = jiffies;
+ INIT_LIST_HEAD(&cal->list);
+
+ return cal;
}
-static void reset_all_loctl_calibration_states(struct b43_wldev *dev)
+/* Get a calibrated LO setting for the given attenuation values.
+ * Might return a NULL pointer under OOM! */
+static
+struct b43_lo_calib * b43_get_calib_lo_settings(struct b43_wldev *dev,
+ const struct b43_bbatt *bbatt,
+ const struct b43_rfatt *rfatt)
{
- b43_call_for_each_loctl(dev, do_reset_calib);
+ struct b43_txpower_lo_control *lo = dev->phy.lo_control;
+ struct b43_lo_calib *c;
+
+ c = b43_find_lo_calib(lo, bbatt, rfatt);
+ if (c)
+ return c;
+ /* Not in the list of calibrated LO settings.
+ * Calibrate it now. */
+ c = b43_calibrate_lo_setting(dev, bbatt, rfatt);
+ if (!c)
+ return NULL;
+ list_add(&c->list, &lo->calib_list);
+
+ return c;
}
-#else /* B43_DEBUG */
-static inline void validate_all_loctls(struct b43_wldev *dev) { }
-static inline void reset_all_loctl_calibration_states(struct b43_wldev *dev) { }
-#endif /* B43_DEBUG */
-
-void b43_lo_g_measure(struct b43_wldev *dev)
+void b43_gphy_dc_lt_init(struct b43_wldev *dev, bool update_all)
{
struct b43_phy *phy = &dev->phy;
- struct lo_g_saved_values uninitialized_var(sav);
-
- B43_WARN_ON((phy->type != B43_PHYTYPE_B) &&
- (phy->type != B43_PHYTYPE_G));
-
- sav.old_channel = phy->channel;
- lo_measure_setup(dev, &sav);
- reset_all_loctl_calibration_states(dev);
- lo_measure(dev);
- lo_measure_restore(dev, &sav);
-
- validate_all_loctls(dev);
+ struct b43_txpower_lo_control *lo = phy->lo_control;
+ int i;
+ int rf_offset, bb_offset;
+ const struct b43_rfatt *rfatt;
+ const struct b43_bbatt *bbatt;
+ u64 power_vector;
+ bool table_changed = 0;
- phy->lo_control->lo_measured = 1;
- phy->lo_control->rebuild = 0;
-}
+ BUILD_BUG_ON(B43_DC_LT_SIZE != 32);
+ B43_WARN_ON(lo->rfatt_list.len * lo->bbatt_list.len > 64);
-#if B43_DEBUG
-static void validate_loctl_calibration(struct b43_wldev *dev,
- struct b43_loctl *loctl,
- struct b43_rfatt *rfatt,
- struct b43_bbatt *bbatt)
-{
- if (b43_loctl_is_calibrated(loctl))
- return;
- if (!dev->phy.lo_control->lo_measured) {
- /* On init we set the attenuation values before we
- * calibrated the LO. I guess that's OK. */
- return;
+ power_vector = lo->power_vector;
+ if (!update_all && !power_vector)
+ return; /* Nothing to do. */
+
+ /* Suspend the MAC now to avoid continuous suspend/enable
+ * cycles in the loop. */
+ b43_mac_suspend(dev);
+
+ for (i = 0; i < B43_DC_LT_SIZE * 2; i++) {
+ struct b43_lo_calib *cal;
+ int idx;
+ u16 val;
+
+ if (!update_all && !(power_vector & (((u64)1ULL) << i)))
+ continue;
+ /* Update the table entry for this power_vector bit.
+ * The table rows are RFatt entries and columns are BBatt. */
+ bb_offset = i / lo->rfatt_list.len;
+ rf_offset = i % lo->rfatt_list.len;
+ bbatt = &(lo->bbatt_list.list[bb_offset]);
+ rfatt = &(lo->rfatt_list.list[rf_offset]);
+
+ cal = b43_calibrate_lo_setting(dev, bbatt, rfatt);
+ if (!cal) {
+ b43warn(dev->wl, "LO: Could not "
+ "calibrate DC table entry\n");
+ continue;
+ }
+ /*FIXME: Is Q really in the low nibble? */
+ val = (u8)(cal->ctl.q);
+ val |= ((u8)(cal->ctl.i)) << 4;
+ kfree(cal);
+
+ /* Get the index into the hardware DC LT. */
+ idx = i / 2;
+ /* Change the table in memory. */
+ if (i % 2) {
+ /* Change the high byte. */
+ lo->dc_lt[idx] = (lo->dc_lt[idx] & 0x00FF)
+ | ((val & 0x00FF) << 8);
+ } else {
+ /* Change the low byte. */
+ lo->dc_lt[idx] = (lo->dc_lt[idx] & 0xFF00)
+ | (val & 0x00FF);
+ }
+ table_changed = 1;
}
- b43err(dev->wl, "Adjusting Local Oscillator to an uncalibrated "
- "control pair: rfatt=%u,%spadmix bbatt=%u\n",
- rfatt->att,
- (rfatt->with_padmix) ? "" : "no-",
- bbatt->att);
-}
-#else
-static inline void validate_loctl_calibration(struct b43_wldev *dev,
- struct b43_loctl *loctl,
- struct b43_rfatt *rfatt,
- struct b43_bbatt *bbatt)
-{
+ if (table_changed) {
+ /* The table changed in memory. Update the hardware table. */
+ for (i = 0; i < B43_DC_LT_SIZE; i++)
+ b43_phy_write(dev, 0x3A0 + i, lo->dc_lt[i]);
+ }
+ b43_mac_enable(dev);
}
-#endif
-static inline void fixup_rfatt_for_txcontrol(struct b43_rfatt *rf,
- u8 tx_control)
+/* Fixup the RF attenuation value for the case where we are
+ * using the PAD mixer. */
+static inline void b43_lo_fixup_rfatt(struct b43_rfatt *rf)
{
- if (tx_control & B43_TXCTL_TXMIX) {
- if (rf->att < 5)
- rf->att = 4;
- }
+ if (!rf->with_padmix)
+ return;
+ if ((rf->att != 1) && (rf->att != 2) && (rf->att != 3))
+ rf->att = 4;
}
void b43_lo_g_adjust(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
+ struct b43_lo_calib *cal;
struct b43_rfatt rf;
- struct b43_loctl *loctl;
memcpy(&rf, &phy->rfatt, sizeof(rf));
- fixup_rfatt_for_txcontrol(&rf, phy->tx_control);
+ b43_lo_fixup_rfatt(&rf);
- loctl = b43_get_lo_g_ctl(dev, &rf, &phy->bbatt);
- validate_loctl_calibration(dev, loctl, &rf, &phy->bbatt);
- b43_lo_write(dev, loctl);
+ cal = b43_get_calib_lo_settings(dev, &phy->bbatt, &rf);
+ if (!cal)
+ return;
+ b43_lo_write(dev, &cal->ctl);
}
void b43_lo_g_adjust_to(struct b43_wldev *dev,
{
struct b43_rfatt rf;
struct b43_bbatt bb;
- struct b43_loctl *loctl;
+ struct b43_lo_calib *cal;
memset(&rf, 0, sizeof(rf));
memset(&bb, 0, sizeof(bb));
rf.att = rfatt;
bb.att = bbatt;
- fixup_rfatt_for_txcontrol(&rf, tx_control);
- loctl = b43_get_lo_g_ctl(dev, &rf, &bb);
- validate_loctl_calibration(dev, loctl, &rf, &bb);
- b43_lo_write(dev, loctl);
+ b43_lo_fixup_rfatt(&rf);
+ cal = b43_get_calib_lo_settings(dev, &bb, &rf);
+ if (!cal)
+ return;
+ b43_lo_write(dev, &cal->ctl);
}
-static void do_mark_unused(struct b43_wldev *dev, struct b43_loctl *control)
+/* Periodic LO maintanance work */
+void b43_lo_g_maintanance_work(struct b43_wldev *dev)
{
- control->used = 0;
+ struct b43_phy *phy = &dev->phy;
+ struct b43_txpower_lo_control *lo = phy->lo_control;
+ unsigned long now;
+ unsigned long expire;
+ struct b43_lo_calib *cal, *tmp;
+ bool current_item_expired = 0;
+ bool hwpctl;
+
+ if (!lo)
+ return;
+ now = jiffies;
+ hwpctl = b43_has_hardware_pctl(phy);
+
+ if (hwpctl) {
+ /* Read the power vector and update it, if needed. */
+ expire = now - B43_LO_PWRVEC_EXPIRE;
+ if (time_before(lo->pwr_vec_read_time, expire)) {
+ lo_read_power_vector(dev);
+ b43_gphy_dc_lt_init(dev, 0);
+ }
+ //FIXME Recalc the whole DC table from time to time?
+ }
+
+ if (hwpctl)
+ return;
+ /* Search for expired LO settings. Remove them.
+ * Recalibrate the current setting, if expired. */
+ expire = now - B43_LO_CALIB_EXPIRE;
+ list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) {
+ if (!time_before(cal->calib_time, expire))
+ continue;
+ /* This item expired. */
+ if (b43_compare_bbatt(&cal->bbatt, &phy->bbatt) &&
+ b43_compare_rfatt(&cal->rfatt, &phy->rfatt)) {
+ B43_WARN_ON(current_item_expired);
+ current_item_expired = 1;
+ }
+ if (b43_debug(dev, B43_DBG_LO)) {
+ b43dbg(dev->wl, "LO: Item BB(%u), RF(%u,%u), "
+ "I=%d, Q=%d expired\n",
+ cal->bbatt.att, cal->rfatt.att,
+ cal->rfatt.with_padmix,
+ cal->ctl.i, cal->ctl.q);
+ }
+ list_del(&cal->list);
+ kfree(cal);
+ }
+ if (current_item_expired || unlikely(list_empty(&lo->calib_list))) {
+ /* Recalibrate currently used LO setting. */
+ if (b43_debug(dev, B43_DBG_LO))
+ b43dbg(dev->wl, "LO: Recalibrating current LO setting\n");
+ cal = b43_calibrate_lo_setting(dev, &phy->bbatt, &phy->rfatt);
+ if (cal) {
+ list_add(&cal->list, &lo->calib_list);
+ b43_lo_write(dev, &cal->ctl);
+ } else
+ b43warn(dev->wl, "Failed to recalibrate current LO setting\n");
+ }
}
-void b43_lo_g_ctl_mark_all_unused(struct b43_wldev *dev)
+void b43_lo_g_cleanup(struct b43_wldev *dev)
{
- struct b43_phy *phy = &dev->phy;
- struct b43_txpower_lo_control *lo = phy->lo_control;
+ struct b43_txpower_lo_control *lo = dev->phy.lo_control;
+ struct b43_lo_calib *cal, *tmp;
- b43_call_for_each_loctl(dev, do_mark_unused);
- lo->rebuild = 1;
+ if (!lo)
+ return;
+ list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) {
+ list_del(&cal->list);
+ kfree(cal);
+ }
}
-void b43_lo_g_ctl_mark_cur_used(struct b43_wldev *dev)
+/* LO Initialization */
+void b43_lo_g_init(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
- struct b43_rfatt rf;
- memcpy(&rf, &phy->rfatt, sizeof(rf));
- fixup_rfatt_for_txcontrol(&rf, phy->tx_control);
-
- b43_get_lo_g_ctl(dev, &rf, &phy->bbatt)->used = 1;
+ if (b43_has_hardware_pctl(phy)) {
+ lo_read_power_vector(dev);
+ b43_gphy_dc_lt_init(dev, 1);
+ }
}
/* 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.
- */
+/* This struct holds calibrated LO settings for a set of
+ * Baseband and RF attenuation settings. */
+struct b43_lo_calib {
+ /* The set of attenuation values this set of LO
+ * control values is calibrated for. */
+ struct b43_bbatt bbatt;
+ struct b43_rfatt rfatt;
+ /* The set of control values for the LO. */
+ struct b43_loctl ctl;
+ /* The time when these settings were calibrated (in jiffies) */
+ unsigned long calib_time;
+ /* List. */
+ struct list_head list;
+};
+
+/* Size of the DC Lookup Table in 16bit words. */
+#define B43_DC_LT_SIZE 32
+
+/* Local Oscillator calibration information */
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. */
+ /* Lists of RF and BB attenuation values for this device.
+ * Used for building hardware power control tables. */
struct b43_rfatt_list rfatt_list;
struct b43_bbatt_list bbatt_list;
+ /* The DC Lookup Table is cached in memory here.
+ * Note that this is only used for Hardware Power Control. */
+ u16 dc_lt[B43_DC_LT_SIZE];
+
+ /* List of calibrated control values (struct b43_lo_calib). */
+ struct list_head calib_list;
+ /* Last time the power vector was read (jiffies). */
+ unsigned long pwr_vec_read_time;
+ /* Last time the txctl values were measured (jiffies). */
+ unsigned long txctl_measured_time;
+
/* 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);
+/* Calibration expire timeouts.
+ * Timeouts must be multiple of 15 seconds. To make sure
+ * the item really expired when the 15 second timer hits, we
+ * subtract two additional seconds from the timeout. */
+#define B43_LO_CALIB_EXPIRE (HZ * (30 - 2))
+#define B43_LO_PWRVEC_EXPIRE (HZ * (30 - 2))
+#define B43_LO_TXCTL_EXPIRE (HZ * (180 - 4))
+
/* Adjust the Local Oscillator to the saved attenuation
* and txctl 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);
+void b43_gphy_dc_lt_init(struct b43_wldev *dev, bool update_all);
-/* 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,
- const struct b43_bbatt *bbatt);
+void b43_lo_g_maintanance_work(struct b43_wldev *dev);
+void b43_lo_g_cleanup(struct b43_wldev *dev);
+void b43_lo_g_init(struct b43_wldev *dev);
#endif /* B43_LO_H_ */
#include <linux/wireless.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
+#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <asm/unaligned.h>
#include "main.h"
#include "debugfs.h"
#include "phy.h"
+#include "nphy.h"
#include "dma.h"
+#include "pio.h"
#include "sysfs.h"
#include "xmit.h"
#include "lo.h"
MODULE_AUTHOR("Michael Buesch");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(B43_SUPPORTED_FIRMWARE_ID);
+
static int modparam_bad_frames_preempt;
module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444);
module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
+int b43_modparam_qos = 1;
+module_param_named(qos, b43_modparam_qos, int, 0444);
+MODULE_PARM_DESC(qos, "Enable QOS support (default on)");
+
+static int modparam_btcoex = 1;
+module_param_named(btcoex, modparam_btcoex, int, 0444);
+MODULE_PARM_DESC(btcoex, "Enable Bluetooth coexistance (default on)");
+
+
static const struct ssb_device_id b43_ssb_tbl[] = {
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5),
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 6),
#define b43_g_ratetable (__b43_ratetable + 0)
#define b43_g_ratetable_size 12
-#define CHANTAB_ENT(_chanid, _freq) \
- { \
- .center_freq = (_freq), \
- .hw_value = (_chanid), \
- }
+#define CHAN4G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
static struct ieee80211_channel b43_2ghz_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),
+ CHAN4G(1, 2412, 0),
+ CHAN4G(2, 2417, 0),
+ CHAN4G(3, 2422, 0),
+ CHAN4G(4, 2427, 0),
+ CHAN4G(5, 2432, 0),
+ CHAN4G(6, 2437, 0),
+ CHAN4G(7, 2442, 0),
+ CHAN4G(8, 2447, 0),
+ CHAN4G(9, 2452, 0),
+ CHAN4G(10, 2457, 0),
+ CHAN4G(11, 2462, 0),
+ CHAN4G(12, 2467, 0),
+ CHAN4G(13, 2472, 0),
+ CHAN4G(14, 2484, 0),
+};
+#undef CHAN4G
+
+#define CHAN5G(_channel, _flags) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = 5000 + (5 * (_channel)), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+static struct ieee80211_channel b43_5ghz_nphy_chantable[] = {
+ CHAN5G(32, 0), CHAN5G(34, 0),
+ CHAN5G(36, 0), CHAN5G(38, 0),
+ CHAN5G(40, 0), CHAN5G(42, 0),
+ CHAN5G(44, 0), CHAN5G(46, 0),
+ CHAN5G(48, 0), CHAN5G(50, 0),
+ CHAN5G(52, 0), CHAN5G(54, 0),
+ CHAN5G(56, 0), CHAN5G(58, 0),
+ CHAN5G(60, 0), CHAN5G(62, 0),
+ CHAN5G(64, 0), CHAN5G(66, 0),
+ CHAN5G(68, 0), CHAN5G(70, 0),
+ CHAN5G(72, 0), CHAN5G(74, 0),
+ CHAN5G(76, 0), CHAN5G(78, 0),
+ CHAN5G(80, 0), CHAN5G(82, 0),
+ CHAN5G(84, 0), CHAN5G(86, 0),
+ CHAN5G(88, 0), CHAN5G(90, 0),
+ CHAN5G(92, 0), CHAN5G(94, 0),
+ CHAN5G(96, 0), CHAN5G(98, 0),
+ CHAN5G(100, 0), CHAN5G(102, 0),
+ CHAN5G(104, 0), CHAN5G(106, 0),
+ CHAN5G(108, 0), CHAN5G(110, 0),
+ CHAN5G(112, 0), CHAN5G(114, 0),
+ CHAN5G(116, 0), CHAN5G(118, 0),
+ CHAN5G(120, 0), CHAN5G(122, 0),
+ CHAN5G(124, 0), CHAN5G(126, 0),
+ CHAN5G(128, 0), CHAN5G(130, 0),
+ CHAN5G(132, 0), CHAN5G(134, 0),
+ CHAN5G(136, 0), CHAN5G(138, 0),
+ CHAN5G(140, 0), CHAN5G(142, 0),
+ CHAN5G(144, 0), CHAN5G(145, 0),
+ CHAN5G(146, 0), CHAN5G(147, 0),
+ CHAN5G(148, 0), CHAN5G(149, 0),
+ CHAN5G(150, 0), CHAN5G(151, 0),
+ CHAN5G(152, 0), CHAN5G(153, 0),
+ CHAN5G(154, 0), CHAN5G(155, 0),
+ CHAN5G(156, 0), CHAN5G(157, 0),
+ CHAN5G(158, 0), CHAN5G(159, 0),
+ CHAN5G(160, 0), CHAN5G(161, 0),
+ CHAN5G(162, 0), CHAN5G(163, 0),
+ CHAN5G(164, 0), CHAN5G(165, 0),
+ CHAN5G(166, 0), CHAN5G(168, 0),
+ CHAN5G(170, 0), CHAN5G(172, 0),
+ CHAN5G(174, 0), CHAN5G(176, 0),
+ CHAN5G(178, 0), CHAN5G(180, 0),
+ CHAN5G(182, 0), CHAN5G(184, 0),
+ CHAN5G(186, 0), CHAN5G(188, 0),
+ CHAN5G(190, 0), CHAN5G(192, 0),
+ CHAN5G(194, 0), CHAN5G(196, 0),
+ CHAN5G(198, 0), CHAN5G(200, 0),
+ CHAN5G(202, 0), CHAN5G(204, 0),
+ CHAN5G(206, 0), CHAN5G(208, 0),
+ CHAN5G(210, 0), CHAN5G(212, 0),
+ CHAN5G(214, 0), CHAN5G(216, 0),
+ CHAN5G(218, 0), CHAN5G(220, 0),
+ CHAN5G(222, 0), CHAN5G(224, 0),
+ CHAN5G(226, 0), CHAN5G(228, 0),
};
-#ifdef NOTYET
-static struct ieee80211_channel b43_5ghz_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 struct ieee80211_channel b43_5ghz_aphy_chantable[] = {
+ CHAN5G(34, 0), CHAN5G(36, 0),
+ CHAN5G(38, 0), CHAN5G(40, 0),
+ CHAN5G(42, 0), CHAN5G(44, 0),
+ CHAN5G(46, 0), CHAN5G(48, 0),
+ CHAN5G(52, 0), CHAN5G(56, 0),
+ CHAN5G(60, 0), CHAN5G(64, 0),
+ CHAN5G(100, 0), CHAN5G(104, 0),
+ CHAN5G(108, 0), CHAN5G(112, 0),
+ CHAN5G(116, 0), CHAN5G(120, 0),
+ CHAN5G(124, 0), CHAN5G(128, 0),
+ CHAN5G(132, 0), CHAN5G(136, 0),
+ CHAN5G(140, 0), CHAN5G(149, 0),
+ CHAN5G(153, 0), CHAN5G(157, 0),
+ CHAN5G(161, 0), CHAN5G(165, 0),
+ CHAN5G(184, 0), CHAN5G(188, 0),
+ CHAN5G(192, 0), CHAN5G(196, 0),
+ CHAN5G(200, 0), CHAN5G(204, 0),
+ CHAN5G(208, 0), CHAN5G(212, 0),
+ CHAN5G(216, 0),
+};
+#undef CHAN5G
+
+static struct ieee80211_supported_band b43_band_5GHz_nphy = {
+ .band = IEEE80211_BAND_5GHZ,
+ .channels = b43_5ghz_nphy_chantable,
+ .n_channels = ARRAY_SIZE(b43_5ghz_nphy_chantable),
+ .bitrates = b43_a_ratetable,
+ .n_bitrates = b43_a_ratetable_size,
};
-static struct ieee80211_supported_band b43_band_5GHz = {
- .channels = b43_5ghz_chantable,
- .n_channels = ARRAY_SIZE(b43_5ghz_chantable),
- .bitrates = b43_a_ratetable,
- .n_bitrates = b43_a_ratetable_size,
+static struct ieee80211_supported_band b43_band_5GHz_aphy = {
+ .band = IEEE80211_BAND_5GHZ,
+ .channels = b43_5ghz_aphy_chantable,
+ .n_channels = ARRAY_SIZE(b43_5ghz_aphy_chantable),
+ .bitrates = b43_a_ratetable,
+ .n_bitrates = b43_a_ratetable_size,
};
-#endif
static struct ieee80211_supported_band b43_band_2GHz = {
- .channels = b43_2ghz_chantable,
- .n_channels = ARRAY_SIZE(b43_2ghz_chantable),
- .bitrates = b43_g_ratetable,
- .n_bitrates = b43_g_ratetable_size,
+ .band = IEEE80211_BAND_2GHZ,
+ .channels = b43_2ghz_chantable,
+ .n_channels = ARRAY_SIZE(b43_2ghz_chantable),
+ .bitrates = b43_g_ratetable,
+ .n_bitrates = b43_g_ratetable_size,
};
static void b43_wireless_core_exit(struct b43_wldev *dev);
}
/* Read HostFlags */
-u32 b43_hf_read(struct b43_wldev * dev)
+u64 b43_hf_read(struct b43_wldev * dev)
{
- u32 ret;
+ u64 ret;
ret = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFHI);
ret <<= 16;
+ ret |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFMI);
+ ret <<= 16;
ret |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFLO);
return ret;
}
/* Write HostFlags */
-void b43_hf_write(struct b43_wldev *dev, u32 value)
+void b43_hf_write(struct b43_wldev *dev, u64 value)
{
- b43_shm_write16(dev, B43_SHM_SHARED,
- B43_SHM_SH_HOSTFLO, (value & 0x0000FFFF));
- b43_shm_write16(dev, B43_SHM_SHARED,
- B43_SHM_SH_HOSTFHI, ((value & 0xFFFF0000) >> 16));
+ u16 lo, mi, hi;
+
+ lo = (value & 0x00000000FFFFULL);
+ mi = (value & 0x0000FFFF0000ULL) >> 16;
+ hi = (value & 0xFFFF00000000ULL) >> 32;
+ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFLO, lo);
+ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFMI, mi);
+ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFHI, hi);
}
void b43_tsf_read(struct b43_wldev *dev, u64 * tsf)
*/
void b43_dummy_transmission(struct b43_wldev *dev)
{
+ struct b43_wl *wl = dev->wl;
struct b43_phy *phy = &dev->phy;
unsigned int i, max_loop;
u16 value;
return;
}
+ spin_lock_irq(&wl->irq_lock);
+ write_lock(&wl->tx_lock);
+
for (i = 0; i < 5; i++)
b43_ram_write(dev, i * 4, buffer[i]);
}
if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5)
b43_radio_write16(dev, 0x0051, 0x0037);
+
+ write_unlock(&wl->tx_lock);
+ spin_unlock_irq(&wl->irq_lock);
}
static void key_write(struct b43_wldev *dev,
/* Turn the Analog ON/OFF */
static void b43_switch_analog(struct b43_wldev *dev, int on)
{
- b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4);
+ switch (dev->phy.type) {
+ case B43_PHYTYPE_A:
+ case B43_PHYTYPE_G:
+ b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4);
+ break;
+ case B43_PHYTYPE_N:
+ b43_phy_write(dev, B43_NPHY_AFECTL_OVER,
+ on ? 0 : 0x7FFF);
+ break;
+ default:
+ B43_WARN_ON(1);
+ }
}
void b43_wireless_core_reset(struct b43_wldev *dev, u32 flags)
/* Get the noise samples. */
B43_WARN_ON(dev->noisecalc.nr_samples >= 8);
i = dev->noisecalc.nr_samples;
- noise[0] = limit_value(noise[0], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
- noise[1] = limit_value(noise[1], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
- noise[2] = limit_value(noise[2], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
- noise[3] = limit_value(noise[3], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
+ noise[0] = clamp_val(noise[0], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
+ noise[1] = clamp_val(noise[1], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
+ noise[2] = clamp_val(noise[2], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
+ noise[3] = clamp_val(noise[3], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
dev->noisecalc.samples[i][0] = phy->nrssi_lt[noise[0]];
dev->noisecalc.samples[i][1] = phy->nrssi_lt[noise[1]];
dev->noisecalc.samples[i][2] = phy->nrssi_lt[noise[2]];
size + sizeof(struct b43_plcp_hdr6));
}
+/* Check if the use of the antenna that ieee80211 told us to
+ * use is possible. This will fall back to DEFAULT.
+ * "antenna_nr" is the antenna identifier we got from ieee80211. */
+u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev,
+ u8 antenna_nr)
+{
+ u8 antenna_mask;
+
+ if (antenna_nr == 0) {
+ /* Zero means "use default antenna". That's always OK. */
+ return 0;
+ }
+
+ /* Get the mask of available antennas. */
+ if (dev->phy.gmode)
+ antenna_mask = dev->dev->bus->sprom.ant_available_bg;
+ else
+ antenna_mask = dev->dev->bus->sprom.ant_available_a;
+
+ if (!(antenna_mask & (1 << (antenna_nr - 1)))) {
+ /* This antenna is not available. Fall back to default. */
+ return 0;
+ }
+
+ return antenna_nr;
+}
+
+static int b43_antenna_from_ieee80211(struct b43_wldev *dev, u8 antenna)
+{
+ antenna = b43_ieee80211_antenna_sanitize(dev, antenna);
+ switch (antenna) {
+ case 0: /* default/diversity */
+ return B43_ANTENNA_DEFAULT;
+ case 1: /* Antenna 0 */
+ return B43_ANTENNA0;
+ case 2: /* Antenna 1 */
+ return B43_ANTENNA1;
+ case 3: /* Antenna 2 */
+ return B43_ANTENNA2;
+ case 4: /* Antenna 3 */
+ return B43_ANTENNA3;
+ default:
+ return B43_ANTENNA_DEFAULT;
+ }
+}
+
+/* Convert a b43 antenna number value to the PHY TX control value. */
+static u16 b43_antenna_to_phyctl(int antenna)
+{
+ switch (antenna) {
+ case B43_ANTENNA0:
+ return B43_TXH_PHY_ANT0;
+ case B43_ANTENNA1:
+ return B43_TXH_PHY_ANT1;
+ case B43_ANTENNA2:
+ return B43_TXH_PHY_ANT2;
+ case B43_ANTENNA3:
+ return B43_TXH_PHY_ANT3;
+ case B43_ANTENNA_AUTO:
+ return B43_TXH_PHY_ANT01AUTO;
+ }
+ B43_WARN_ON(1);
+ return 0;
+}
+
static void b43_write_beacon_template(struct b43_wldev *dev,
u16 ram_offset,
- u16 shm_size_offset, u8 rate)
+ u16 shm_size_offset)
{
unsigned int i, len, variable_len;
const struct ieee80211_mgmt *bcn;
const u8 *ie;
bool tim_found = 0;
+ unsigned int rate;
+ u16 ctl;
+ int antenna;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon);
bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data);
len = min((size_t) dev->wl->current_beacon->len,
0x200 - sizeof(struct b43_plcp_hdr6));
+ rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value;
b43_write_template_common(dev, (const u8 *)bcn,
len, ram_offset, shm_size_offset, rate);
+ /* Write the PHY TX control parameters. */
+ antenna = b43_antenna_from_ieee80211(dev, info->antenna_sel_tx);
+ antenna = b43_antenna_to_phyctl(antenna);
+ ctl = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL);
+ /* We can't send beacons with short preamble. Would get PHY errors. */
+ ctl &= ~B43_TXH_PHY_SHORTPRMBL;
+ ctl &= ~B43_TXH_PHY_ANT;
+ ctl &= ~B43_TXH_PHY_ENC;
+ ctl |= antenna;
+ if (b43_is_cck_rate(rate))
+ ctl |= B43_TXH_PHY_ENC_CCK;
+ else
+ ctl |= B43_TXH_PHY_ENC_OFDM;
+ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, ctl);
+
/* Find the position of the TIM and the DTIM_period value
* and write them to SHM. */
ie = bcn->u.beacon.variable;
b43warn(dev->wl, "Did not find a valid TIM IE in "
"the beacon template packet. AP or IBSS operation "
"may be broken.\n");
- }
+ } else
+ b43dbg(dev->wl, "Updated beacon template\n");
}
static void b43_write_probe_resp_plcp(struct b43_wldev *dev,
kfree(probe_resp_data);
}
+static void handle_irq_beacon(struct b43_wldev *dev)
+{
+ struct b43_wl *wl = dev->wl;
+ u32 cmd, beacon0_valid, beacon1_valid;
+
+ if (!b43_is_mode(wl, IEEE80211_IF_TYPE_AP))
+ return;
+
+ /* This is the bottom half of the asynchronous beacon update. */
+
+ /* Ignore interrupt in the future. */
+ dev->irq_savedstate &= ~B43_IRQ_BEACON;
+
+ cmd = b43_read32(dev, B43_MMIO_MACCMD);
+ beacon0_valid = (cmd & B43_MACCMD_BEACON0_VALID);
+ beacon1_valid = (cmd & B43_MACCMD_BEACON1_VALID);
+
+ /* Schedule interrupt manually, if busy. */
+ if (beacon0_valid && beacon1_valid) {
+ b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_BEACON);
+ dev->irq_savedstate |= B43_IRQ_BEACON;
+ return;
+ }
+
+ if (!beacon0_valid) {
+ if (!wl->beacon0_uploaded) {
+ b43_write_beacon_template(dev, 0x68, 0x18);
+ b43_write_probe_resp_template(dev, 0x268, 0x4A,
+ &__b43_ratetable[3]);
+ wl->beacon0_uploaded = 1;
+ }
+ cmd = b43_read32(dev, B43_MMIO_MACCMD);
+ cmd |= B43_MACCMD_BEACON0_VALID;
+ b43_write32(dev, B43_MMIO_MACCMD, cmd);
+ } else if (!beacon1_valid) {
+ if (!wl->beacon1_uploaded) {
+ b43_write_beacon_template(dev, 0x468, 0x1A);
+ wl->beacon1_uploaded = 1;
+ }
+ cmd = b43_read32(dev, B43_MMIO_MACCMD);
+ cmd |= B43_MACCMD_BEACON1_VALID;
+ b43_write32(dev, B43_MMIO_MACCMD, cmd);
+ }
+}
+
+static void b43_beacon_update_trigger_work(struct work_struct *work)
+{
+ struct b43_wl *wl = container_of(work, struct b43_wl,
+ beacon_update_trigger);
+ struct b43_wldev *dev;
+
+ mutex_lock(&wl->mutex);
+ dev = wl->current_dev;
+ if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) {
+ spin_lock_irq(&wl->irq_lock);
+ /* update beacon right away or defer to irq */
+ dev->irq_savedstate = b43_read32(dev, B43_MMIO_GEN_IRQ_MASK);
+ handle_irq_beacon(dev);
+ /* The handler might have updated the IRQ mask. */
+ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK,
+ dev->irq_savedstate);
+ mmiowb();
+ spin_unlock_irq(&wl->irq_lock);
+ }
+ mutex_unlock(&wl->mutex);
+}
+
/* Asynchronously update the packet templates in template RAM.
* Locking: Requires wl->irq_lock to be locked. */
static void b43_update_templates(struct b43_wl *wl, struct sk_buff *beacon)
wl->current_beacon = beacon;
wl->beacon0_uploaded = 0;
wl->beacon1_uploaded = 0;
+ queue_work(wl->hw->workqueue, &wl->beacon_update_trigger);
}
static void b43_set_ssid(struct b43_wldev *dev, const u8 * ssid, u8 ssid_len)
{
b43_time_lock(dev);
if (dev->dev->id.revision >= 3) {
- b43_write32(dev, 0x188, (beacon_int << 16));
+ b43_write32(dev, B43_MMIO_TSF_CFP_REP, (beacon_int << 16));
+ b43_write32(dev, B43_MMIO_TSF_CFP_START, (beacon_int << 10));
} else {
b43_write16(dev, 0x606, (beacon_int >> 6));
b43_write16(dev, 0x610, beacon_int);
}
b43_time_unlock(dev);
+ b43dbg(dev->wl, "Set beacon interval to %u\n", beacon_int);
}
-static void handle_irq_beacon(struct b43_wldev *dev)
+static void b43_handle_firmware_panic(struct b43_wldev *dev)
{
- struct b43_wl *wl = dev->wl;
- u32 cmd;
+ u16 reason;
- if (!b43_is_mode(wl, IEEE80211_IF_TYPE_AP))
- return;
+ /* Read the register that contains the reason code for the panic. */
+ reason = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_FWPANIC_REASON_REG);
+ b43err(dev->wl, "Whoopsy, firmware panic! Reason: %u\n", reason);
- /* This is the bottom half of the asynchronous beacon update. */
-
- cmd = b43_read32(dev, B43_MMIO_MACCMD);
- if (!(cmd & B43_MACCMD_BEACON0_VALID)) {
- if (!wl->beacon0_uploaded) {
- b43_write_beacon_template(dev, 0x68, 0x18,
- B43_CCK_RATE_1MB);
- b43_write_probe_resp_template(dev, 0x268, 0x4A,
- &__b43_ratetable[3]);
- wl->beacon0_uploaded = 1;
- }
- cmd |= B43_MACCMD_BEACON0_VALID;
- }
- if (!(cmd & B43_MACCMD_BEACON1_VALID)) {
- if (!wl->beacon1_uploaded) {
- b43_write_beacon_template(dev, 0x468, 0x1A,
- B43_CCK_RATE_1MB);
- wl->beacon1_uploaded = 1;
- }
- cmd |= B43_MACCMD_BEACON1_VALID;
+ switch (reason) {
+ default:
+ b43dbg(dev->wl, "The panic reason is unknown.\n");
+ /* fallthrough */
+ case B43_FWPANIC_DIE:
+ /* Do not restart the controller or firmware.
+ * The device is nonfunctional from now on.
+ * Restarting would result in this panic to trigger again,
+ * so we avoid that recursion. */
+ break;
+ case B43_FWPANIC_RESTART:
+ b43_controller_restart(dev, "Microcode panic");
+ break;
}
- b43_write32(dev, B43_MMIO_MACCMD, cmd);
}
static void handle_irq_ucode_debug(struct b43_wldev *dev)
{
- //TODO
+ unsigned int i, cnt;
+ u16 reason, marker_id, marker_line;
+ __le16 *buf;
+
+ /* The proprietary firmware doesn't have this IRQ. */
+ if (!dev->fw.opensource)
+ return;
+
+ /* Read the register that contains the reason code for this IRQ. */
+ reason = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_DEBUGIRQ_REASON_REG);
+
+ switch (reason) {
+ case B43_DEBUGIRQ_PANIC:
+ b43_handle_firmware_panic(dev);
+ break;
+ case B43_DEBUGIRQ_DUMP_SHM:
+ if (!B43_DEBUG)
+ break; /* Only with driver debugging enabled. */
+ buf = kmalloc(4096, GFP_ATOMIC);
+ if (!buf) {
+ b43dbg(dev->wl, "SHM-dump: Failed to allocate memory\n");
+ goto out;
+ }
+ for (i = 0; i < 4096; i += 2) {
+ u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, i);
+ buf[i / 2] = cpu_to_le16(tmp);
+ }
+ b43info(dev->wl, "Shared memory dump:\n");
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET,
+ 16, 2, buf, 4096, 1);
+ kfree(buf);
+ break;
+ case B43_DEBUGIRQ_DUMP_REGS:
+ if (!B43_DEBUG)
+ break; /* Only with driver debugging enabled. */
+ b43info(dev->wl, "Microcode register dump:\n");
+ for (i = 0, cnt = 0; i < 64; i++) {
+ u16 tmp = b43_shm_read16(dev, B43_SHM_SCRATCH, i);
+ if (cnt == 0)
+ printk(KERN_INFO);
+ printk("r%02u: 0x%04X ", i, tmp);
+ cnt++;
+ if (cnt == 6) {
+ printk("\n");
+ cnt = 0;
+ }
+ }
+ printk("\n");
+ break;
+ case B43_DEBUGIRQ_MARKER:
+ if (!B43_DEBUG)
+ break; /* Only with driver debugging enabled. */
+ marker_id = b43_shm_read16(dev, B43_SHM_SCRATCH,
+ B43_MARKER_ID_REG);
+ marker_line = b43_shm_read16(dev, B43_SHM_SCRATCH,
+ B43_MARKER_LINE_REG);
+ b43info(dev->wl, "The firmware just executed the MARKER(%u) "
+ "at line number %u\n",
+ marker_id, marker_line);
+ break;
+ default:
+ b43dbg(dev->wl, "Debug-IRQ triggered for unknown reason: %u\n",
+ reason);
+ }
+out:
+ /* Acknowledge the debug-IRQ, so the firmware can continue. */
+ b43_shm_write16(dev, B43_SHM_SCRATCH,
+ B43_DEBUGIRQ_REASON_REG, B43_DEBUGIRQ_ACK);
}
/* Interrupt handler bottom-half */
handle_irq_noise(dev);
/* Check the DMA reason registers for received data. */
- if (dma_reason[0] & B43_DMAIRQ_RX_DONE)
- b43_dma_rx(dev->dma.rx_ring0);
- if (dma_reason[3] & B43_DMAIRQ_RX_DONE)
- b43_dma_rx(dev->dma.rx_ring3);
+ if (dma_reason[0] & B43_DMAIRQ_RX_DONE) {
+ if (b43_using_pio_transfers(dev))
+ b43_pio_rx(dev->pio.rx_queue);
+ else
+ b43_dma_rx(dev->dma.rx_ring);
+ }
B43_WARN_ON(dma_reason[1] & B43_DMAIRQ_RX_DONE);
B43_WARN_ON(dma_reason[2] & B43_DMAIRQ_RX_DONE);
+ B43_WARN_ON(dma_reason[3] & B43_DMAIRQ_RX_DONE);
B43_WARN_ON(dma_reason[4] & B43_DMAIRQ_RX_DONE);
B43_WARN_ON(dma_reason[5] & B43_DMAIRQ_RX_DONE);
static int do_request_fw(struct b43_wldev *dev,
const char *name,
- struct b43_firmware_file *fw)
+ struct b43_firmware_file *fw,
+ bool silent)
{
char path[sizeof(modparam_fwpostfix) + 32];
const struct firmware *blob;
"b43%s/%s.fw",
modparam_fwpostfix, name);
err = request_firmware(&blob, path, dev->dev->dev);
- if (err) {
- b43err(dev->wl, "Firmware file \"%s\" not found "
- "or load failed.\n", path);
+ if (err == -ENOENT) {
+ if (!silent) {
+ b43err(dev->wl, "Firmware file \"%s\" not found\n",
+ path);
+ }
+ return err;
+ } else if (err) {
+ b43err(dev->wl, "Firmware file \"%s\" request failed (err=%d)\n",
+ path, err);
return err;
}
if (blob->size < sizeof(struct b43_fw_header))
filename = "ucode13";
else
goto err_no_ucode;
- err = do_request_fw(dev, filename, &fw->ucode);
+ err = do_request_fw(dev, filename, &fw->ucode, 0);
if (err)
goto err_load;
filename = NULL;
else
goto err_no_pcm;
- err = do_request_fw(dev, filename, &fw->pcm);
- if (err)
+ fw->pcm_request_failed = 0;
+ err = do_request_fw(dev, filename, &fw->pcm, 1);
+ if (err == -ENOENT) {
+ /* We did not find a PCM file? Not fatal, but
+ * core rev <= 10 must do without hwcrypto then. */
+ fw->pcm_request_failed = 1;
+ } else if (err)
goto err_load;
/* Get initvals */
if ((rev >= 5) && (rev <= 10))
filename = "b0g0initvals5";
else if (rev >= 13)
- filename = "lp0initvals13";
+ filename = "b0g0initvals13";
else
goto err_no_initvals;
break;
default:
goto err_no_initvals;
}
- err = do_request_fw(dev, filename, &fw->initvals);
+ err = do_request_fw(dev, filename, &fw->initvals, 0);
if (err)
goto err_load;
default:
goto err_no_initvals;
}
- err = do_request_fw(dev, filename, &fw->initvals_band);
+ err = do_request_fw(dev, filename, &fw->initvals_band, 0);
if (err)
goto err_load;
err = -EOPNOTSUPP;
goto error;
}
- b43dbg(dev->wl, "Loading firmware version %u.%u "
- "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n",
- fwrev, fwpatch,
- (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF,
- (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, fwtime & 0x1F);
-
dev->fw.rev = fwrev;
dev->fw.patch = fwpatch;
+ dev->fw.opensource = (fwdate == 0xFFFF);
+
+ if (dev->fw.opensource) {
+ /* Patchlevel info is encoded in the "time" field. */
+ dev->fw.patch = fwtime;
+ b43info(dev->wl, "Loading OpenSource firmware version %u.%u%s\n",
+ dev->fw.rev, dev->fw.patch,
+ dev->fw.pcm_request_failed ? " (Hardware crypto not supported)" : "");
+ } else {
+ b43info(dev->wl, "Loading firmware version %u.%u "
+ "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n",
+ fwrev, fwpatch,
+ (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF,
+ (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, fwtime & 0x1F);
+ if (dev->fw.pcm_request_failed) {
+ b43warn(dev->wl, "No \"pcm5.fw\" firmware file found. "
+ "Hardware accelerated cryptography is disabled.\n");
+ b43_print_fw_helptext(dev->wl, 0);
+ }
+ }
if (b43_is_old_txhdr_format(dev)) {
b43warn(dev->wl, "You are using an old firmware image. "
goto err_format;
array_size -= sizeof(iv->data.d32);
- value = be32_to_cpu(get_unaligned(&iv->data.d32));
+ value = get_unaligned_be32(&iv->data.d32);
b43_write32(dev, offset, value);
iv = (const struct b43_iv *)((const uint8_t *)iv +
{
dev->mac_suspended--;
B43_WARN_ON(dev->mac_suspended < 0);
- B43_WARN_ON(irqs_disabled());
if (dev->mac_suspended == 0) {
b43_write32(dev, B43_MMIO_MACCTL,
b43_read32(dev, B43_MMIO_MACCTL)
b43_read32(dev, B43_MMIO_MACCTL);
b43_read32(dev, B43_MMIO_GEN_IRQ_REASON);
b43_power_saving_ctl_bits(dev, 0);
-
- /* Re-enable IRQs. */
- spin_lock_irq(&dev->wl->irq_lock);
- b43_interrupt_enable(dev, dev->irq_savedstate);
- spin_unlock_irq(&dev->wl->irq_lock);
}
}
u32 tmp;
might_sleep();
- B43_WARN_ON(irqs_disabled());
B43_WARN_ON(dev->mac_suspended < 0);
if (dev->mac_suspended == 0) {
- /* Mask IRQs before suspending MAC. Otherwise
- * the MAC stays busy and won't suspend. */
- spin_lock_irq(&dev->wl->irq_lock);
- tmp = b43_interrupt_disable(dev, B43_IRQ_ALL);
- spin_unlock_irq(&dev->wl->irq_lock);
- b43_synchronize_irq(dev);
- dev->irq_savedstate = tmp;
-
b43_power_saving_ctl_bits(dev, B43_PS_AWAKE);
b43_write32(dev, B43_MMIO_MACCTL,
b43_read32(dev, B43_MMIO_MACCTL)
& ~B43_MACCTL_ENABLED);
/* force pci to flush the write */
b43_read32(dev, B43_MMIO_MACCTL);
+ for (i = 35; i; i--) {
+ tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON);
+ if (tmp & B43_IRQ_MAC_SUSPENDED)
+ goto out;
+ udelay(10);
+ }
+ /* Hm, it seems this will take some time. Use msleep(). */
for (i = 40; i; i--) {
tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON);
if (tmp & B43_IRQ_MAC_SUSPENDED)
}
}
+/* Set the default values for the PHY TX Control Words. */
+static void b43_set_phytxctl_defaults(struct b43_wldev *dev)
+{
+ u16 ctl = 0;
+
+ ctl |= B43_TXH_PHY_ENC_CCK;
+ ctl |= B43_TXH_PHY_ANT01AUTO;
+ ctl |= B43_TXH_PHY_TXPWR;
+
+ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, ctl);
+ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL, ctl);
+ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL, ctl);
+}
+
/* Set the TX-Antenna for management frames sent by firmware. */
static void b43_mgmtframe_txantenna(struct b43_wldev *dev, int antenna)
{
- u16 ant = 0;
+ u16 ant;
u16 tmp;
- switch (antenna) {
- case B43_ANTENNA0:
- ant |= B43_TXH_PHY_ANT0;
- break;
- case B43_ANTENNA1:
- ant |= B43_TXH_PHY_ANT1;
- break;
- case B43_ANTENNA2:
- ant |= B43_TXH_PHY_ANT2;
- break;
- case B43_ANTENNA3:
- ant |= B43_TXH_PHY_ANT3;
- break;
- case B43_ANTENNA_AUTO:
- ant |= B43_TXH_PHY_ANT01AUTO;
- break;
- default:
- B43_WARN_ON(1);
- }
-
- /* FIXME We also need to set the other flags of the PHY control field somewhere. */
+ ant = b43_antenna_to_phyctl(antenna);
- /* For Beacons */
- tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL);
- tmp = (tmp & ~B43_TXH_PHY_ANT) | ant;
- b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, tmp);
/* For ACK/CTS */
tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL);
tmp = (tmp & ~B43_TXH_PHY_ANT) | ant;
{
b43_radio_turn_off(dev, 1);
b43_gpio_cleanup(dev);
+ b43_lo_g_cleanup(dev);
/* firmware is released later */
}
return err;
}
-static void b43_periodic_every120sec(struct b43_wldev *dev)
-{
- struct b43_phy *phy = &dev->phy;
-
- if (phy->type != B43_PHYTYPE_G || phy->rev < 2)
- return;
-
- b43_mac_suspend(dev);
- b43_lo_g_measure(dev);
- b43_mac_enable(dev);
- if (b43_has_hardware_pctl(phy))
- b43_lo_g_ctl_mark_all_unused(dev);
-}
-
static void b43_periodic_every60sec(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
if (phy->type != B43_PHYTYPE_G)
return;
- if (!b43_has_hardware_pctl(phy))
- b43_lo_g_ctl_mark_all_unused(dev);
if (dev->dev->bus->sprom.boardflags_lo & B43_BFL_RSSI) {
b43_mac_suspend(dev);
b43_calc_nrssi_slope(dev);
}
}
b43_phy_xmitpower(dev); //FIXME: unless scanning?
+ b43_lo_g_maintanance_work(dev);
//TODO for APHY (temperature?)
atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT);
unsigned int state;
state = dev->periodic_state;
- if (state % 8 == 0)
- b43_periodic_every120sec(dev);
if (state % 4 == 0)
b43_periodic_every60sec(dev);
if (state % 2 == 0)
}
static int b43_op_tx(struct ieee80211_hw *hw,
- struct sk_buff *skb,
- struct ieee80211_tx_control *ctl)
+ struct sk_buff *skb)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev = wl->current_dev;
- int err = -ENODEV;
+ unsigned long flags;
+ int err;
+ if (unlikely(skb->len < 2 + 2 + 6)) {
+ /* Too short, this can't be a valid frame. */
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+ B43_WARN_ON(skb_shinfo(skb)->nr_frags);
if (unlikely(!dev))
- goto out;
- if (unlikely(b43_status(dev) < B43_STAT_STARTED))
- goto out;
- /* DMA-TX is done without a global lock. */
- err = b43_dma_tx(dev, skb, ctl);
-out:
+ return NETDEV_TX_BUSY;
+
+ /* Transmissions on seperate queues can run concurrently. */
+ read_lock_irqsave(&wl->tx_lock, flags);
+
+ err = -ENODEV;
+ if (likely(b43_status(dev) >= B43_STAT_STARTED)) {
+ if (b43_using_pio_transfers(dev))
+ err = b43_pio_tx(dev, skb);
+ else
+ err = b43_dma_tx(dev, skb);
+ }
+
+ read_unlock_irqrestore(&wl->tx_lock, flags);
+
if (unlikely(err))
return NETDEV_TX_BUSY;
return NETDEV_TX_OK;
}
-static int b43_op_conf_tx(struct ieee80211_hw *hw,
- int queue,
+/* Locking: wl->irq_lock */
+static void b43_qos_params_upload(struct b43_wldev *dev,
+ const struct ieee80211_tx_queue_params *p,
+ u16 shm_offset)
+{
+ u16 params[B43_NR_QOSPARAMS];
+ int cw_min, cw_max, aifs, bslots, tmp;
+ unsigned int i;
+
+ const u16 aCWmin = 0x0001;
+ const u16 aCWmax = 0x03FF;
+
+ /* Calculate the default values for the parameters, if needed. */
+ switch (shm_offset) {
+ case B43_QOS_VOICE:
+ aifs = (p->aifs == -1) ? 2 : p->aifs;
+ cw_min = (p->cw_min == 0) ? ((aCWmin + 1) / 4 - 1) : p->cw_min;
+ cw_max = (p->cw_max == 0) ? ((aCWmin + 1) / 2 - 1) : p->cw_max;
+ break;
+ case B43_QOS_VIDEO:
+ aifs = (p->aifs == -1) ? 2 : p->aifs;
+ cw_min = (p->cw_min == 0) ? ((aCWmin + 1) / 2 - 1) : p->cw_min;
+ cw_max = (p->cw_max == 0) ? aCWmin : p->cw_max;
+ break;
+ case B43_QOS_BESTEFFORT:
+ aifs = (p->aifs == -1) ? 3 : p->aifs;
+ cw_min = (p->cw_min == 0) ? aCWmin : p->cw_min;
+ cw_max = (p->cw_max == 0) ? aCWmax : p->cw_max;
+ break;
+ case B43_QOS_BACKGROUND:
+ aifs = (p->aifs == -1) ? 7 : p->aifs;
+ cw_min = (p->cw_min == 0) ? aCWmin : p->cw_min;
+ cw_max = (p->cw_max == 0) ? aCWmax : p->cw_max;
+ break;
+ default:
+ B43_WARN_ON(1);
+ return;
+ }
+ if (cw_min <= 0)
+ cw_min = aCWmin;
+ if (cw_max <= 0)
+ cw_max = aCWmin;
+ bslots = b43_read16(dev, B43_MMIO_RNG) % cw_min;
+
+ memset(¶ms, 0, sizeof(params));
+
+ params[B43_QOSPARAM_TXOP] = p->txop * 32;
+ params[B43_QOSPARAM_CWMIN] = cw_min;
+ params[B43_QOSPARAM_CWMAX] = cw_max;
+ params[B43_QOSPARAM_CWCUR] = cw_min;
+ params[B43_QOSPARAM_AIFS] = aifs;
+ params[B43_QOSPARAM_BSLOTS] = bslots;
+ params[B43_QOSPARAM_REGGAP] = bslots + aifs;
+
+ for (i = 0; i < ARRAY_SIZE(params); i++) {
+ if (i == B43_QOSPARAM_STATUS) {
+ tmp = b43_shm_read16(dev, B43_SHM_SHARED,
+ shm_offset + (i * 2));
+ /* Mark the parameters as updated. */
+ tmp |= 0x100;
+ b43_shm_write16(dev, B43_SHM_SHARED,
+ shm_offset + (i * 2),
+ tmp);
+ } else {
+ b43_shm_write16(dev, B43_SHM_SHARED,
+ shm_offset + (i * 2),
+ params[i]);
+ }
+ }
+}
+
+/* Update the QOS parameters in hardware. */
+static void b43_qos_update(struct b43_wldev *dev)
+{
+ struct b43_wl *wl = dev->wl;
+ struct b43_qos_params *params;
+ unsigned long flags;
+ unsigned int i;
+
+ /* Mapping of mac80211 queues to b43 SHM offsets. */
+ static const u16 qos_shm_offsets[] = {
+ [0] = B43_QOS_VOICE,
+ [1] = B43_QOS_VIDEO,
+ [2] = B43_QOS_BESTEFFORT,
+ [3] = B43_QOS_BACKGROUND,
+ };
+ BUILD_BUG_ON(ARRAY_SIZE(qos_shm_offsets) != ARRAY_SIZE(wl->qos_params));
+
+ b43_mac_suspend(dev);
+ spin_lock_irqsave(&wl->irq_lock, flags);
+
+ for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
+ params = &(wl->qos_params[i]);
+ if (params->need_hw_update) {
+ b43_qos_params_upload(dev, &(params->p),
+ qos_shm_offsets[i]);
+ params->need_hw_update = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&wl->irq_lock, flags);
+ b43_mac_enable(dev);
+}
+
+static void b43_qos_clear(struct b43_wl *wl)
+{
+ struct b43_qos_params *params;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
+ params = &(wl->qos_params[i]);
+
+ memset(&(params->p), 0, sizeof(params->p));
+ params->p.aifs = -1;
+ params->need_hw_update = 1;
+ }
+}
+
+/* Initialize the core's QOS capabilities */
+static void b43_qos_init(struct b43_wldev *dev)
+{
+ struct b43_wl *wl = dev->wl;
+ unsigned int i;
+
+ /* Upload the current QOS parameters. */
+ for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++)
+ wl->qos_params[i].need_hw_update = 1;
+ b43_qos_update(dev);
+
+ /* Enable QOS support. */
+ b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF);
+ b43_write16(dev, B43_MMIO_IFSCTL,
+ b43_read16(dev, B43_MMIO_IFSCTL)
+ | B43_MMIO_IFSCTL_USE_EDCF);
+}
+
+static void b43_qos_update_work(struct work_struct *work)
+{
+ struct b43_wl *wl = container_of(work, struct b43_wl, qos_update_work);
+ struct b43_wldev *dev;
+
+ mutex_lock(&wl->mutex);
+ dev = wl->current_dev;
+ if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED)))
+ b43_qos_update(dev);
+ mutex_unlock(&wl->mutex);
+}
+
+static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue,
const struct ieee80211_tx_queue_params *params)
{
+ struct b43_wl *wl = hw_to_b43_wl(hw);
+ unsigned long flags;
+ unsigned int queue = (unsigned int)_queue;
+ struct b43_qos_params *p;
+
+ if (queue >= ARRAY_SIZE(wl->qos_params)) {
+ /* Queue not available or don't support setting
+ * params on this queue. Return success to not
+ * confuse mac80211. */
+ return 0;
+ }
+
+ spin_lock_irqsave(&wl->irq_lock, flags);
+ p = &(wl->qos_params[queue]);
+ memcpy(&(p->p), params, sizeof(p->p));
+ p->need_hw_update = 1;
+ spin_unlock_irqrestore(&wl->irq_lock, flags);
+
+ queue_work(hw->workqueue, &wl->qos_update_work);
+
return 0;
}
goto out;
spin_lock_irqsave(&wl->irq_lock, flags);
if (likely(b43_status(dev) >= B43_STAT_STARTED)) {
- b43_dma_get_tx_stats(dev, stats);
+ if (b43_using_pio_transfers(dev))
+ b43_pio_get_tx_stats(dev, stats);
+ else
+ b43_dma_get_tx_stats(dev, stats);
err = 0;
}
spin_unlock_irqrestore(&wl->irq_lock, flags);
return 0;
}
-static const char *phymode_to_string(unsigned int phymode)
-{
- switch (phymode) {
- case B43_PHYMODE_A:
- return "A";
- case B43_PHYMODE_B:
- return "B";
- case B43_PHYMODE_G:
- return "G";
- default:
- B43_WARN_ON(1);
- }
- return "";
-}
-
-static int find_wldev_for_phymode(struct b43_wl *wl,
- unsigned int phymode,
- struct b43_wldev **dev, bool * gmode)
-{
- struct b43_wldev *d;
-
- list_for_each_entry(d, &wl->devlist, list) {
- if (d->phy.possible_phymodes & phymode) {
- /* Ok, this device supports the PHY-mode.
- * Now figure out how the gmode bit has to be
- * set to support it. */
- if (phymode == B43_PHYMODE_A)
- *gmode = 0;
- else
- *gmode = 1;
- *dev = d;
-
- return 0;
- }
- }
-
- return -ESRCH;
-}
-
static void b43_put_phy_into_reset(struct b43_wldev *dev)
{
struct ssb_device *sdev = dev->dev;
msleep(1);
}
+static const char * band_to_string(enum ieee80211_band band)
+{
+ switch (band) {
+ case IEEE80211_BAND_5GHZ:
+ return "5";
+ case IEEE80211_BAND_2GHZ:
+ return "2.4";
+ default:
+ break;
+ }
+ B43_WARN_ON(1);
+ return "";
+}
+
/* Expects wl->mutex locked */
-static int b43_switch_phymode(struct b43_wl *wl, unsigned int new_mode)
+static int b43_switch_band(struct b43_wl *wl, struct ieee80211_channel *chan)
{
- struct b43_wldev *up_dev;
+ struct b43_wldev *up_dev = NULL;
struct b43_wldev *down_dev;
+ struct b43_wldev *d;
int err;
- bool gmode = 0;
+ bool gmode;
int prev_status;
- err = find_wldev_for_phymode(wl, new_mode, &up_dev, &gmode);
- if (err) {
- b43err(wl, "Could not find a device for %s-PHY mode\n",
- phymode_to_string(new_mode));
- return err;
+ /* Find a device and PHY which supports the band. */
+ list_for_each_entry(d, &wl->devlist, list) {
+ switch (chan->band) {
+ case IEEE80211_BAND_5GHZ:
+ if (d->phy.supports_5ghz) {
+ up_dev = d;
+ gmode = 0;
+ }
+ break;
+ case IEEE80211_BAND_2GHZ:
+ if (d->phy.supports_2ghz) {
+ up_dev = d;
+ gmode = 1;
+ }
+ break;
+ default:
+ B43_WARN_ON(1);
+ return -EINVAL;
+ }
+ if (up_dev)
+ break;
+ }
+ if (!up_dev) {
+ b43err(wl, "Could not find a device for %s-GHz band operation\n",
+ band_to_string(chan->band));
+ return -ENODEV;
}
if ((up_dev == wl->current_dev) &&
(!!wl->current_dev->phy.gmode == !!gmode)) {
/* This device is already running. */
return 0;
}
- b43dbg(wl, "Reconfiguring PHYmode to %s-PHY\n",
- phymode_to_string(new_mode));
+ b43dbg(wl, "Switching to %s-GHz band\n",
+ band_to_string(chan->band));
down_dev = wl->current_dev;
prev_status = b43_status(down_dev);
err = b43_wireless_core_init(up_dev);
if (err) {
b43err(wl, "Fatal: Could not initialize device for "
- "newly selected %s-PHY mode\n",
- phymode_to_string(new_mode));
+ "selected %s-GHz band\n",
+ band_to_string(chan->band));
goto init_failure;
}
}
err = b43_wireless_core_start(up_dev);
if (err) {
b43err(wl, "Fatal: Coult not start device for "
- "newly selected %s-PHY mode\n",
- phymode_to_string(new_mode));
+ "selected %s-GHz band\n",
+ band_to_string(chan->band));
b43_wireless_core_exit(up_dev);
goto init_failure;
}
wl->current_dev = up_dev;
return 0;
- init_failure:
+init_failure:
/* Whoops, failed to init the new core. No core is operating now. */
wl->current_dev = NULL;
return err;
}
-/* Check if the use of the antenna that ieee80211 told us to
- * use is possible. This will fall back to DEFAULT.
- * "antenna_nr" is the antenna identifier we got from ieee80211. */
-u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev,
- u8 antenna_nr)
-{
- u8 antenna_mask;
-
- if (antenna_nr == 0) {
- /* Zero means "use default antenna". That's always OK. */
- return 0;
- }
-
- /* Get the mask of available antennas. */
- if (dev->phy.gmode)
- antenna_mask = dev->dev->bus->sprom.ant_available_bg;
- else
- antenna_mask = dev->dev->bus->sprom.ant_available_a;
-
- if (!(antenna_mask & (1 << (antenna_nr - 1)))) {
- /* This antenna is not available. Fall back to default. */
- return 0;
- }
-
- return antenna_nr;
-}
-
-static int b43_antenna_from_ieee80211(struct b43_wldev *dev, u8 antenna)
-{
- antenna = b43_ieee80211_antenna_sanitize(dev, antenna);
- switch (antenna) {
- case 0: /* default/diversity */
- return B43_ANTENNA_DEFAULT;
- case 1: /* Antenna 0 */
- return B43_ANTENNA0;
- case 2: /* Antenna 1 */
- return B43_ANTENNA1;
- case 3: /* Antenna 2 */
- return B43_ANTENNA2;
- case 4: /* Antenna 3 */
- return B43_ANTENNA3;
- default:
- return B43_ANTENNA_DEFAULT;
- }
-}
-
static int b43_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev;
struct b43_phy *phy;
unsigned long flags;
- unsigned int new_phymode = 0xFFFF;
int antenna;
int err = 0;
u32 savedirqs;
mutex_lock(&wl->mutex);
- /* Switch the PHY mode (if necessary). */
- switch (conf->channel->band) {
- case IEEE80211_BAND_5GHZ:
- new_phymode = B43_PHYMODE_A;
- break;
- case IEEE80211_BAND_2GHZ:
- new_phymode = B43_PHYMODE_G;
- break;
- default:
- B43_WARN_ON(1);
- }
- err = b43_switch_phymode(wl, new_phymode);
+ /* Switch the band (if necessary). This might change the active core. */
+ err = b43_switch_band(wl, conf->channel);
if (err)
goto out_unlock_mutex;
dev = wl->current_dev;
if (!dev || b43_status(dev) < B43_STAT_INITIALIZED)
goto out_unlock;
+ if (dev->fw.pcm_request_failed) {
+ /* We don't have firmware for the crypto engine.
+ * Must use software-crypto. */
+ err = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+
err = -EINVAL;
switch (key->alg) {
case ALG_WEP:
spin_unlock_irqrestore(&wl->irq_lock, flags);
b43_synchronize_irq(dev);
+ write_lock_irqsave(&wl->tx_lock, flags);
b43_set_status(dev, B43_STAT_INITIALIZED);
+ write_unlock_irqrestore(&wl->tx_lock, flags);
+ b43_pio_stop(dev);
mutex_unlock(&wl->mutex);
/* Must unlock as it would otherwise deadlock. No races here.
* Cancel the possibly running self-rearming periodic work. */
cancel_delayed_work_sync(&dev->periodic_work);
mutex_lock(&wl->mutex);
- ieee80211_stop_queues(wl->hw); //FIXME this could cause a deadlock, as mac80211 seems buggy.
-
b43_mac_suspend(dev);
free_irq(dev->dev->irq, dev);
b43dbg(wl, "Wireless interface stopped\n");
/* Start data flow (TX/RX). */
b43_mac_enable(dev);
b43_interrupt_enable(dev, dev->irq_savedstate);
- ieee80211_start_queues(dev->wl->hw);
/* Start maintainance work */
b43_periodic_tasks_setup(dev);
lo = phy->lo_control;
if (lo) {
memset(lo, 0, sizeof(*(phy->lo_control)));
- lo->rebuild = 1;
lo->tx_bias = 0xFF;
+ INIT_LIST_HEAD(&lo->calib_list);
}
phy->max_lb_gain = 0;
phy->trsw_rx_gain = 0;
static void b43_bluetooth_coext_enable(struct b43_wldev *dev)
{
struct ssb_sprom *sprom = &dev->dev->bus->sprom;
- u32 hf;
+ u64 hf;
+ if (!modparam_btcoex)
+ return;
if (!(sprom->boardflags_lo & B43_BFL_BTCOEXIST))
return;
if (dev->phy.type != B43_PHYTYPE_B && !dev->phy.gmode)
else
hf |= B43_HF_BTCOEX;
b43_hf_write(dev, hf);
- //TODO
}
static void b43_bluetooth_coext_disable(struct b43_wldev *dev)
-{ //TODO
+{
+ if (!modparam_btcoex)
+ return;
+ //TODO
}
static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev)
long_retry);
}
+static void b43_set_synth_pu_delay(struct b43_wldev *dev, bool idle)
+{
+ u16 pu_delay;
+
+ /* The time value is in microseconds. */
+ if (dev->phy.type == B43_PHYTYPE_A)
+ pu_delay = 3700;
+ else
+ pu_delay = 1050;
+ if (b43_is_mode(dev->wl, IEEE80211_IF_TYPE_IBSS) || idle)
+ pu_delay = 500;
+ if ((dev->phy.radio_ver == 0x2050) && (dev->phy.radio_rev == 8))
+ pu_delay = max(pu_delay, (u16)2400);
+
+ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_SPUWKUP, pu_delay);
+}
+
+/* Set the TSF CFP pre-TargetBeaconTransmissionTime. */
+static void b43_set_pretbtt(struct b43_wldev *dev)
+{
+ u16 pretbtt;
+
+ /* The time value is in microseconds. */
+ if (b43_is_mode(dev->wl, IEEE80211_IF_TYPE_IBSS)) {
+ pretbtt = 2;
+ } else {
+ if (dev->phy.type == B43_PHYTYPE_A)
+ pretbtt = 120;
+ else
+ pretbtt = 250;
+ }
+ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRETBTT, pretbtt);
+ b43_write16(dev, B43_MMIO_TSF_CFP_PRETBTT, pretbtt);
+}
+
/* Shutdown a wireless core */
/* Locking: wl->mutex */
static void b43_wireless_core_exit(struct b43_wldev *dev)
macctl |= B43_MACCTL_PSM_JMP0;
b43_write32(dev, B43_MMIO_MACCTL, macctl);
- b43_leds_exit(dev);
- b43_rng_exit(dev->wl);
+ if (!dev->suspend_in_progress) {
+ b43_leds_exit(dev);
+ b43_rng_exit(dev->wl);
+ }
b43_dma_free(dev);
+ b43_pio_free(dev);
b43_chip_exit(dev);
b43_radio_turn_off(dev, 1);
b43_switch_analog(dev, 0);
struct ssb_sprom *sprom = &bus->sprom;
struct b43_phy *phy = &dev->phy;
int err;
- u32 hf, tmp;
+ u64 hf;
+ u32 tmp;
B43_WARN_ON(b43_status(dev) != B43_STAT_UNINIT);
b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRMAXTIME, 1);
b43_rate_memory_init(dev);
+ b43_set_phytxctl_defaults(dev);
/* Minimum Contention Window */
if (phy->type == B43_PHYTYPE_B) {
/* Maximum Contention Window */
b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF);
- err = b43_dma_init(dev);
+ if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || B43_FORCE_PIO) {
+ dev->__using_pio_transfers = 1;
+ err = b43_pio_init(dev);
+ } else {
+ dev->__using_pio_transfers = 0;
+ err = b43_dma_init(dev);
+ }
if (err)
goto err_chip_exit;
b43_qos_init(dev);
-
-//FIXME
-#if 1
- b43_write16(dev, 0x0612, 0x0050);
- b43_shm_write16(dev, B43_SHM_SHARED, 0x0416, 0x0050);
- b43_shm_write16(dev, B43_SHM_SHARED, 0x0414, 0x01F4);
-#endif
-
+ b43_set_synth_pu_delay(dev, 1);
b43_bluetooth_coext_enable(dev);
ssb_bus_powerup(bus, 1); /* Enable dynamic PCTL */
b43_upload_card_macaddress(dev);
b43_security_init(dev);
- b43_rng_init(wl);
+ if (!dev->suspend_in_progress)
+ b43_rng_init(wl);
b43_set_status(dev, B43_STAT_INITIALIZED);
- b43_leds_init(dev);
+ if (!dev->suspend_in_progress)
+ b43_leds_init(dev);
out:
return err;
spin_lock_irqsave(&wl->irq_lock, flags);
b43_adjust_opmode(dev);
+ b43_set_pretbtt(dev);
+ b43_set_synth_pu_delay(dev, 0);
b43_upload_card_macaddress(dev);
spin_unlock_irqrestore(&wl->irq_lock, flags);
memset(wl->mac_addr, 0, ETH_ALEN);
wl->filter_flags = 0;
wl->radiotap_enabled = 0;
+ b43_qos_clear(wl);
/* First register RFkill.
* LEDs that are registered later depend on it. */
struct b43_wldev *dev = wl->current_dev;
b43_rfkill_exit(dev);
+ cancel_work_sync(&(wl->qos_update_work));
+ cancel_work_sync(&(wl->beacon_update_trigger));
mutex_lock(&wl->mutex);
if (b43_status(dev) >= B43_STAT_STARTED)
* the TIM field, but that would probably require resizing and
* moving of data within the beacon template.
* Simply request a new beacon and let mac80211 do the hard work. */
- beacon = ieee80211_beacon_get(hw, wl->vif, NULL);
+ beacon = ieee80211_beacon_get(hw, wl->vif);
if (unlikely(!beacon))
return -ENOMEM;
spin_lock_irqsave(&wl->irq_lock, flags);
}
static int b43_op_ibss_beacon_update(struct ieee80211_hw *hw,
- struct sk_buff *beacon,
- struct ieee80211_tx_control *ctl)
+ struct sk_buff *beacon)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
unsigned long flags;
return 0;
}
+static void b43_op_sta_notify(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ const u8 *addr)
+{
+ struct b43_wl *wl = hw_to_b43_wl(hw);
+
+ B43_WARN_ON(!vif || wl->vif != vif);
+}
+
static const struct ieee80211_ops b43_hw_ops = {
.tx = b43_op_tx,
.conf_tx = b43_op_conf_tx,
.set_retry_limit = b43_op_set_retry_limit,
.set_tim = b43_op_beacon_set_tim,
.beacon_update = b43_op_ibss_beacon_update,
+ .sta_notify = b43_op_sta_notify,
};
/* Hard-reset the chip. Do not call this directly.
b43info(wl, "Controller restarted\n");
}
-static int b43_setup_modes(struct b43_wldev *dev,
+static int b43_setup_bands(struct b43_wldev *dev,
bool have_2ghz_phy, bool have_5ghz_phy)
{
struct ieee80211_hw *hw = dev->wl->hw;
- struct b43_phy *phy = &dev->phy;
- /* XXX: This function will go away soon, when mac80211
- * band stuff is rewritten. So this is just a hack.
- * For now we always claim GPHY mode, as there is no
- * support for NPHY and APHY in the device, yet.
- * This assumption is OK, as any B, N or A PHY will already
- * have died a horrible sanity check death earlier. */
+ if (have_2ghz_phy)
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &b43_band_2GHz;
+ if (dev->phy.type == B43_PHYTYPE_N) {
+ if (have_5ghz_phy)
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_nphy;
+ } else {
+ if (have_5ghz_phy)
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_aphy;
+ }
- hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &b43_band_2GHz;
- phy->possible_phymodes |= B43_PHYMODE_G;
+ dev->phy.supports_2ghz = have_2ghz_phy;
+ dev->phy.supports_5ghz = have_5ghz_phy;
return 0;
}
err = -EOPNOTSUPP;
goto err_powerdown;
}
+ if (1 /* disable A-PHY */) {
+ /* FIXME: For now we disable the A-PHY on multi-PHY devices. */
+ if (dev->phy.type != B43_PHYTYPE_N) {
+ have_2ghz_phy = 1;
+ have_5ghz_phy = 0;
+ }
+ }
+
dev->phy.gmode = have_2ghz_phy;
tmp = dev->phy.gmode ? B43_TMSLOW_GMODE : 0;
b43_wireless_core_reset(dev, tmp);
err = b43_validate_chipaccess(dev);
if (err)
goto err_powerdown;
- err = b43_setup_modes(dev, have_2ghz_phy, have_5ghz_phy);
+ err = b43_setup_bands(dev, have_2ghz_phy, have_5ghz_phy);
if (err)
goto err_powerdown;
return err;
}
+#define IS_PDEV(pdev, _vendor, _device, _subvendor, _subdevice) ( \
+ (pdev->vendor == PCI_VENDOR_ID_##_vendor) && \
+ (pdev->device == _device) && \
+ (pdev->subsystem_vendor == PCI_VENDOR_ID_##_subvendor) && \
+ (pdev->subsystem_device == _subdevice) )
+
static void b43_sprom_fixup(struct ssb_bus *bus)
{
+ struct pci_dev *pdev;
+
/* boardflags workarounds */
if (bus->boardinfo.vendor == SSB_BOARDVENDOR_DELL &&
bus->chip_id == 0x4301 && bus->boardinfo.rev == 0x74)
if (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE &&
bus->boardinfo.type == 0x4E && bus->boardinfo.rev > 0x40)
bus->sprom.boardflags_lo |= B43_BFL_PACTRL;
+ if (bus->bustype == SSB_BUSTYPE_PCI) {
+ pdev = bus->host_pci;
+ if (IS_PDEV(pdev, BROADCOM, 0x4318, ASUSTEK, 0x100F) ||
+ IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0015) ||
+ IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0013))
+ bus->sprom.boardflags_lo &= ~B43_BFL_BTCOEXIST;
+ }
}
static void b43_wireless_exit(struct ssb_device *dev, struct b43_wl *wl)
/* fill hw info */
hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE |
- IEEE80211_HW_RX_INCLUDES_FCS;
- hw->max_signal = 100;
- hw->max_rssi = -110;
- hw->max_noise = -110;
- hw->queues = 1; /* FIXME: hardware has more queues */
+ IEEE80211_HW_RX_INCLUDES_FCS |
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_NOISE_DBM;
+
+ hw->queues = b43_modparam_qos ? 4 : 1;
SET_IEEE80211_DEV(hw, dev->dev);
if (is_valid_ether_addr(sprom->et1mac))
SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac);
memset(wl, 0, sizeof(*wl));
wl->hw = hw;
spin_lock_init(&wl->irq_lock);
+ rwlock_init(&wl->tx_lock);
spin_lock_init(&wl->leds_lock);
spin_lock_init(&wl->shm_lock);
mutex_init(&wl->mutex);
INIT_LIST_HEAD(&wl->devlist);
+ INIT_WORK(&wl->qos_update_work, b43_qos_update_work);
+ INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work);
ssb_set_devtypedata(dev, wl);
b43info(wl, "Broadcom %04X WLAN found\n", dev->bus->chip_id);
b43dbg(wl, "Suspending...\n");
mutex_lock(&wl->mutex);
+ wldev->suspend_in_progress = true;
wldev->suspend_init_status = b43_status(wldev);
if (wldev->suspend_init_status >= B43_STAT_STARTED)
b43_wireless_core_stop(wldev);
if (wldev->suspend_init_status >= B43_STAT_STARTED) {
err = b43_wireless_core_start(wldev);
if (err) {
+ b43_leds_exit(wldev);
+ b43_rng_exit(wldev->wl);
b43_wireless_core_exit(wldev);
b43err(wl, "Resume failed at core start\n");
goto out;
}
}
- mutex_unlock(&wl->mutex);
-
b43dbg(wl, "Device resumed.\n");
- out:
+ out:
+ wldev->suspend_in_progress = false;
+ mutex_unlock(&wl->mutex);
return err;
}
.resume = b43_resume,
};
+static void b43_print_driverinfo(void)
+{
+ const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "",
+ *feat_leds = "", *feat_rfkill = "";
+
+#ifdef CONFIG_B43_PCI_AUTOSELECT
+ feat_pci = "P";
+#endif
+#ifdef CONFIG_B43_PCMCIA
+ feat_pcmcia = "M";
+#endif
+#ifdef CONFIG_B43_NPHY
+ feat_nphy = "N";
+#endif
+#ifdef CONFIG_B43_LEDS
+ feat_leds = "L";
+#endif
+#ifdef CONFIG_B43_RFKILL
+ feat_rfkill = "R";
+#endif
+ printk(KERN_INFO "Broadcom 43xx driver loaded "
+ "[ Features: %s%s%s%s%s, Firmware-ID: "
+ B43_SUPPORTED_FIRMWARE_ID " ]\n",
+ feat_pci, feat_pcmcia, feat_nphy,
+ feat_leds, feat_rfkill);
+}
+
static int __init b43_init(void)
{
int err;
err = ssb_driver_register(&b43_ssb_driver);
if (err)
goto err_pcmcia_exit;
+ b43_print_driverinfo();
return err;
/* Magic helper macro to pad structures. Ignore those above. It's magic. */
#define PAD_BYTES(nr_bytes) P4D_BYTES( __LINE__ , (nr_bytes))
+
+extern int b43_modparam_qos;
+
+
/* Lightweight function to convert a frequency (in Mhz) to a channel number. */
static inline u8 b43_freq_to_channel_5ghz(int freq)
{
void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value);
void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value);
-u32 b43_hf_read(struct b43_wldev *dev);
-void b43_hf_write(struct b43_wldev *dev, u32 value);
+u64 b43_hf_read(struct b43_wldev *dev);
+void b43_hf_write(struct b43_wldev *dev, u64 value);
void b43_dummy_transmission(struct b43_wldev *dev);
void b43_wireless_core_reset(struct b43_wldev *dev, u32 flags);
-void b43_mac_suspend(struct b43_wldev *dev);
-void b43_mac_enable(struct b43_wldev *dev);
-
void b43_controller_restart(struct b43_wldev *dev, const char *reason);
#define B43_PS_ENABLED (1 << 0) /* Force enable hardware power saving */
#define B43_PS_ASLEEP (1 << 3) /* Force device asleep */
void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags);
+void b43_mac_suspend(struct b43_wldev *dev);
+void b43_mac_enable(struct b43_wldev *dev);
+
#endif /* B43_MAIN_H_ */
#include "nphy.h"
#include "tables_nphy.h"
-#include <linux/delay.h>
-
void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
{//TODO
b43_phy_set(dev, B43_NPHY_IQFLIP,
B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2);
- //FIXME the following condition is different in the specs.
if (1 /* FIXME band is 2.4GHz */) {
b43_phy_set(dev, B43_NPHY_CLASSCTL,
B43_NPHY_CLASSCTL_CCKEN);
struct b43_wldev;
+
+#ifdef CONFIG_B43_NPHY
+/* N-PHY support enabled */
+
int b43_phy_initn(struct b43_wldev *dev);
void b43_nphy_radio_turn_on(struct b43_wldev *dev);
void b43_nphy_xmitpower(struct b43_wldev *dev);
void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna);
+
+#else /* CONFIG_B43_NPHY */
+/* N-PHY support disabled */
+
+
+static inline
+int b43_phy_initn(struct b43_wldev *dev)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline
+void b43_nphy_radio_turn_on(struct b43_wldev *dev)
+{
+}
+static inline
+void b43_nphy_radio_turn_off(struct b43_wldev *dev)
+{
+}
+
+static inline
+int b43_nphy_selectchannel(struct b43_wldev *dev, u8 channel)
+{
+ return -ENOSYS;
+}
+
+static inline
+void b43_nphy_xmitpower(struct b43_wldev *dev)
+{
+}
+static inline
+void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
+{
+}
+
+#endif /* CONFIG_B43_NPHY */
#endif /* B43_NPHY_H_ */
#ifdef CONFIG_PM
static int b43_pcmcia_suspend(struct pcmcia_device *dev)
{
- //TODO
- return 0;
+ struct ssb_bus *ssb = dev->priv;
+
+ return ssb_bus_suspend(ssb);
}
static int b43_pcmcia_resume(struct pcmcia_device *dev)
{
- //TODO
- return 0;
+ struct ssb_bus *ssb = dev->priv;
+
+ return ssb_bus_resume(ssb);
}
#else /* CONFIG_PM */
# define b43_pcmcia_suspend NULL
dev->conf.ConfigBase = parse.config.base;
dev->conf.Present = parse.config.rmask[0];
+ dev->conf.Attributes = CONF_ENABLE_IRQ;
+ dev->conf.IntType = INT_MEMORY_AND_IO;
dev->io.BasePort2 = 0;
dev->io.NumPorts2 = 0;
if (res != CS_SUCCESS)
goto err_disable;
- dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FIRST_SHARED;
- dev->irq.IRQInfo1 = IRQ_LEVEL_ID | IRQ_SHARE_ID;
+ dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+ dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
dev->irq.Handler = NULL; /* The handler is registered later. */
dev->irq.Instance = NULL;
res = pcmcia_request_irq(dev, &dev->irq);
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/types.h>
+#include <linux/bitrev.h>
#include "b43.h"
#include "phy.h"
72, 84,
};
+#define bitrev4(tmp) (bitrev8(tmp) >> 4)
static void b43_phy_initg(struct b43_wldev *dev);
-/* Reverse the bits of a 4bit value.
- * Example: 1101 is flipped 1011
- */
-static u16 flip_4bit(u16 value)
-{
- u16 flipped = 0x0000;
-
- B43_WARN_ON(value & ~0x000F);
-
- flipped |= (value & 0x0001) << 3;
- flipped |= (value & 0x0002) << 1;
- flipped |= (value & 0x0004) >> 1;
- flipped |= (value & 0x0008) >> 3;
-
- return flipped;
-}
-
static void generate_rfatt_list(struct b43_wldev *dev,
struct b43_rfatt_list *list)
{
{.att = 9,.with_padmix = 1,},
};
- if ((phy->type == B43_PHYTYPE_A && phy->rev < 5) ||
- (phy->type == B43_PHYTYPE_G && phy->rev < 6)) {
+ if (!b43_has_hardware_pctl(phy)) {
/* Software pctl */
list->list = rfatt_0;
list->len = ARRAY_SIZE(rfatt_0);
/* Hardware pctl */
list->list = rfatt_1;
list->len = ARRAY_SIZE(rfatt_1);
- list->min_val = 2;
+ list->min_val = 0;
list->max_val = 14;
return;
}
/* Save the values for later */
phy->tx_control = tx_control;
memcpy(&phy->rfatt, rfatt, sizeof(*rfatt));
+ phy->rfatt.with_padmix = !!(tx_control & B43_TXCTL_TXMIX);
memcpy(&phy->bbatt, bbatt, sizeof(*bbatt));
if (b43_debug(dev, B43_DBG_XMITPOWER)) {
u16 tmp;
u8 rf, bb;
- if (!lo->lo_measured) {
- b43_phy_write(dev, 0x3FF, 0);
- return;
- }
-
for (rf = 0; rf < lo->rfatt_list.len; rf++) {
for (bb = 0; bb < lo->bbatt_list.len; bb++) {
if (nr_written >= 0x40)
}
}
-/* GPHY_DC_Lookup_Table */
-void b43_gphy_dc_lt_init(struct b43_wldev *dev)
-{
- struct b43_phy *phy = &dev->phy;
- struct b43_txpower_lo_control *lo = phy->lo_control;
- struct b43_loctl *loctl0;
- struct b43_loctl *loctl1;
- int i;
- int rf_offset, bb_offset;
- u16 tmp;
-
- for (i = 0; i < lo->rfatt_list.len + lo->bbatt_list.len; i += 2) {
- rf_offset = i / lo->rfatt_list.len;
- bb_offset = i % lo->rfatt_list.len;
-
- loctl0 = b43_get_lo_g_ctl(dev, &lo->rfatt_list.list[rf_offset],
- &lo->bbatt_list.list[bb_offset]);
- if (i + 1 < lo->rfatt_list.len * lo->bbatt_list.len) {
- rf_offset = (i + 1) / lo->rfatt_list.len;
- bb_offset = (i + 1) % lo->rfatt_list.len;
-
- loctl1 =
- b43_get_lo_g_ctl(dev,
- &lo->rfatt_list.list[rf_offset],
- &lo->bbatt_list.list[bb_offset]);
- } else
- loctl1 = loctl0;
-
- tmp = ((u16) loctl0->q & 0xF);
- tmp |= ((u16) loctl0->i & 0xF) << 4;
- tmp |= ((u16) loctl1->q & 0xF) << 8;
- tmp |= ((u16) loctl1->i & 0xF) << 12; //FIXME?
- b43_phy_write(dev, 0x3A0 + (i / 2), tmp);
- }
-}
-
static void hardware_pctl_init_aphy(struct b43_wldev *dev)
{
//TODO
b43_phy_write(dev, 0x0801, b43_phy_read(dev, 0x0801)
& 0xFFBF);
- b43_gphy_dc_lt_init(dev);
+ b43_gphy_dc_lt_init(dev, 1);
}
/* HardwarePowerControl init for A and G PHY */
b43_phy_write(dev, B43_PHY_OFDM(0xBB),
(b43_phy_read(dev, B43_PHY_OFDM(0xBB)) & 0xF000) | 0x0053);
b43_phy_write(dev, B43_PHY_OFDM61,
- (b43_phy_read(dev, B43_PHY_OFDM61 & 0xFE1F)) | 0x0120);
+ (b43_phy_read(dev, B43_PHY_OFDM61) & 0xFE1F) | 0x0120);
b43_phy_write(dev, B43_PHY_OFDM(0x13),
(b43_phy_read(dev, B43_PHY_OFDM(0x13)) & 0x0FFF) | 0x3000);
b43_phy_write(dev, B43_PHY_OFDM(0x14),
}
}
-static void b43_phy_initb2(struct b43_wldev *dev)
-{
- struct b43_phy *phy = &dev->phy;
- u16 offset, val;
-
- b43_write16(dev, 0x03EC, 0x3F22);
- b43_phy_write(dev, 0x0020, 0x301C);
- b43_phy_write(dev, 0x0026, 0x0000);
- b43_phy_write(dev, 0x0030, 0x00C6);
- b43_phy_write(dev, 0x0088, 0x3E00);
- val = 0x3C3D;
- for (offset = 0x0089; offset < 0x00A7; offset++) {
- b43_phy_write(dev, offset, val);
- val -= 0x0202;
- }
- b43_phy_write(dev, 0x03E4, 0x3000);
- b43_radio_selectchannel(dev, phy->channel, 0);
- if (phy->radio_ver != 0x2050) {
- b43_radio_write16(dev, 0x0075, 0x0080);
- b43_radio_write16(dev, 0x0079, 0x0081);
- }
- b43_radio_write16(dev, 0x0050, 0x0020);
- b43_radio_write16(dev, 0x0050, 0x0023);
- if (phy->radio_ver == 0x2050) {
- b43_radio_write16(dev, 0x0050, 0x0020);
- b43_radio_write16(dev, 0x005A, 0x0070);
- b43_radio_write16(dev, 0x005B, 0x007B);
- b43_radio_write16(dev, 0x005C, 0x00B0);
- b43_radio_write16(dev, 0x007A, 0x000F);
- b43_phy_write(dev, 0x0038, 0x0677);
- b43_radio_init2050(dev);
- }
- b43_phy_write(dev, 0x0014, 0x0080);
- b43_phy_write(dev, 0x0032, 0x00CA);
- b43_phy_write(dev, 0x0032, 0x00CC);
- b43_phy_write(dev, 0x0035, 0x07C2);
- b43_lo_b_measure(dev);
- b43_phy_write(dev, 0x0026, 0xCC00);
- if (phy->radio_ver != 0x2050)
- b43_phy_write(dev, 0x0026, 0xCE00);
- b43_write16(dev, B43_MMIO_CHANNEL_EXT, 0x1000);
- b43_phy_write(dev, 0x002A, 0x88A3);
- if (phy->radio_ver != 0x2050)
- b43_phy_write(dev, 0x002A, 0x88C2);
- b43_set_txpower_g(dev, &phy->bbatt, &phy->rfatt, phy->tx_control);
- b43_phy_init_pctl(dev);
-}
-
-static void b43_phy_initb4(struct b43_wldev *dev)
-{
- struct b43_phy *phy = &dev->phy;
- u16 offset, val;
-
- b43_write16(dev, 0x03EC, 0x3F22);
- b43_phy_write(dev, 0x0020, 0x301C);
- b43_phy_write(dev, 0x0026, 0x0000);
- b43_phy_write(dev, 0x0030, 0x00C6);
- b43_phy_write(dev, 0x0088, 0x3E00);
- val = 0x3C3D;
- for (offset = 0x0089; offset < 0x00A7; offset++) {
- b43_phy_write(dev, offset, val);
- val -= 0x0202;
- }
- b43_phy_write(dev, 0x03E4, 0x3000);
- b43_radio_selectchannel(dev, phy->channel, 0);
- if (phy->radio_ver != 0x2050) {
- b43_radio_write16(dev, 0x0075, 0x0080);
- b43_radio_write16(dev, 0x0079, 0x0081);
- }
- b43_radio_write16(dev, 0x0050, 0x0020);
- b43_radio_write16(dev, 0x0050, 0x0023);
- if (phy->radio_ver == 0x2050) {
- b43_radio_write16(dev, 0x0050, 0x0020);
- b43_radio_write16(dev, 0x005A, 0x0070);
- b43_radio_write16(dev, 0x005B, 0x007B);
- b43_radio_write16(dev, 0x005C, 0x00B0);
- b43_radio_write16(dev, 0x007A, 0x000F);
- b43_phy_write(dev, 0x0038, 0x0677);
- b43_radio_init2050(dev);
- }
- b43_phy_write(dev, 0x0014, 0x0080);
- b43_phy_write(dev, 0x0032, 0x00CA);
- if (phy->radio_ver == 0x2050)
- b43_phy_write(dev, 0x0032, 0x00E0);
- b43_phy_write(dev, 0x0035, 0x07C2);
-
- b43_lo_b_measure(dev);
-
- b43_phy_write(dev, 0x0026, 0xCC00);
- if (phy->radio_ver == 0x2050)
- b43_phy_write(dev, 0x0026, 0xCE00);
- b43_write16(dev, B43_MMIO_CHANNEL_EXT, 0x1100);
- b43_phy_write(dev, 0x002A, 0x88A3);
- if (phy->radio_ver == 0x2050)
- b43_phy_write(dev, 0x002A, 0x88C2);
- b43_set_txpower_g(dev, &phy->bbatt, &phy->rfatt, phy->tx_control);
- if (dev->dev->bus->sprom.boardflags_lo & B43_BFL_RSSI) {
- b43_calc_nrssi_slope(dev);
- b43_calc_nrssi_threshold(dev);
- }
- b43_phy_init_pctl(dev);
-}
-
static void b43_phy_initb5(struct b43_wldev *dev)
{
struct ssb_bus *bus = dev->dev->bus;
b43_phy_write(dev, 0x0002, (b43_phy_read(dev, 0x0002) & 0xFFC0)
| 0x0004);
}
- if (phy->type == B43_PHYTYPE_B) {
- b43_write16(dev, 0x03E6, 0x8140);
- b43_phy_write(dev, 0x0016, 0x0410);
- b43_phy_write(dev, 0x0017, 0x0820);
- b43_phy_write(dev, 0x0062, 0x0007);
- b43_radio_init2050(dev);
- b43_lo_g_measure(dev);
- if (dev->dev->bus->sprom.boardflags_lo & B43_BFL_RSSI) {
- b43_calc_nrssi_slope(dev);
- b43_calc_nrssi_threshold(dev);
- }
- b43_phy_init_pctl(dev);
- } else if (phy->type == B43_PHYTYPE_G)
+ if (phy->type == B43_PHYTYPE_B)
+ B43_WARN_ON(1);
+ else if (phy->type == B43_PHYTYPE_G)
b43_write16(dev, 0x03E6, 0x0);
}
else
b43_radio_write16(dev, 0x0078, phy->initval);
}
- if (phy->lo_control->tx_bias == 0xFF) {
- b43_lo_g_measure(dev);
+ b43_lo_g_init(dev);
+ if (has_tx_magnification(phy)) {
+ b43_radio_write16(dev, 0x52,
+ (b43_radio_read16(dev, 0x52) & 0xFF00)
+ | phy->lo_control->tx_bias | phy->
+ lo_control->tx_magn);
} else {
- if (has_tx_magnification(phy)) {
- b43_radio_write16(dev, 0x52,
- (b43_radio_read16(dev, 0x52) & 0xFF00)
- | phy->lo_control->tx_bias | phy->
- lo_control->tx_magn);
- } else {
- b43_radio_write16(dev, 0x52,
- (b43_radio_read16(dev, 0x52) & 0xFFF0)
- | phy->lo_control->tx_bias);
- }
- if (phy->rev >= 6) {
- b43_phy_write(dev, B43_PHY_CCK(0x36),
- (b43_phy_read(dev, B43_PHY_CCK(0x36))
- & 0x0FFF) | (phy->lo_control->
- tx_bias << 12));
- }
- if (dev->dev->bus->sprom.boardflags_lo & B43_BFL_PACTRL)
- b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8075);
- else
- b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x807F);
- if (phy->rev < 2)
- b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x101);
- else
- b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x202);
+ b43_radio_write16(dev, 0x52,
+ (b43_radio_read16(dev, 0x52) & 0xFFF0)
+ | phy->lo_control->tx_bias);
}
+ if (phy->rev >= 6) {
+ b43_phy_write(dev, B43_PHY_CCK(0x36),
+ (b43_phy_read(dev, B43_PHY_CCK(0x36))
+ & 0x0FFF) | (phy->lo_control->
+ tx_bias << 12));
+ }
+ if (dev->dev->bus->sprom.boardflags_lo & B43_BFL_PACTRL)
+ b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8075);
+ else
+ b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x807F);
+ if (phy->rev < 2)
+ b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x101);
+ else
+ b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x202);
if (phy->gmode || phy->rev >= 2) {
b43_lo_g_adjust(dev);
b43_phy_write(dev, B43_PHY_LO_MASK, 0x8078);
* the value 0x7FFFFFFF here. I think that is some weird
* compiler optimization in the original driver.
* Essentially, what we do here is resetting all NRSSI LT
- * entries to -32 (see the limit_value() in nrssi_hw_update())
+ * entries to -32 (see the clamp_val() in nrssi_hw_update())
*/
b43_nrssi_hw_update(dev, 0xFFFF); //FIXME?
b43_calc_nrssi_threshold(dev);
switch (phy->type) {
case B43_PHYTYPE_A:
tmp += 0x80;
- tmp = limit_value(tmp, 0x00, 0xFF);
+ tmp = clamp_val(tmp, 0x00, 0xFF);
dbm = phy->tssi2dbm[tmp];
//TODO: There's a FIXME on the specs
break;
case B43_PHYTYPE_B:
case B43_PHYTYPE_G:
- tmp = limit_value(tmp, 0x00, 0x3F);
+ tmp = clamp_val(tmp, 0x00, 0x3F);
dbm = phy->tssi2dbm[tmp];
break;
default:
break;
}
- *_rfatt = limit_value(rfatt, rf_min, rf_max);
- *_bbatt = limit_value(bbatt, bb_min, bb_max);
+ *_rfatt = clamp_val(rfatt, rf_min, rf_max);
+ *_bbatt = clamp_val(bbatt, bb_min, bb_max);
}
/* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */
/* Get desired power (in Q5.2) */
desired_pwr = INT_TO_Q52(phy->power_level);
/* And limit it. max_pwr already is Q5.2 */
- desired_pwr = limit_value(desired_pwr, 0, max_pwr);
+ desired_pwr = clamp_val(desired_pwr, 0, max_pwr);
if (b43_debug(dev, B43_DBG_XMITPOWER)) {
b43dbg(dev->wl,
"Current TX power output: " Q52_FMT
bbatt_delta -= 4 * rfatt_delta;
/* So do we finally need to adjust something? */
- if ((rfatt_delta == 0) && (bbatt_delta == 0)) {
- b43_lo_g_ctl_mark_cur_used(dev);
+ if ((rfatt_delta == 0) && (bbatt_delta == 0))
return;
- }
/* Calculate the new attenuation values. */
bbatt = phy->bbatt.att;
b43_radio_lock(dev);
b43_set_txpower_g(dev, &phy->bbatt, &phy->rfatt,
phy->tx_control);
- b43_lo_g_ctl_mark_cur_used(dev);
b43_radio_unlock(dev);
b43_phy_unlock(dev);
break;
f = q;
i++;
} while (delta >= 2);
- entry[index] = limit_value(b43_tssi2dbm_ad(m1 * f, 8192), -127, 128);
+ entry[index] = clamp_val(b43_tssi2dbm_ad(m1 * f, 8192), -127, 128);
return 0;
}
else
unsupported = 1;
break;
- case B43_PHYTYPE_B:
- switch (phy->rev) {
- case 2:
- b43_phy_initb2(dev);
- break;
- case 4:
- b43_phy_initb4(dev);
- break;
- case 5:
- b43_phy_initb5(dev);
- break;
- case 6:
- b43_phy_initb6(dev);
- break;
- default:
- unsupported = 1;
- }
- break;
case B43_PHYTYPE_G:
b43_phy_initg(dev);
break;
void b43_set_rx_antenna(struct b43_wldev *dev, int antenna)
{
struct b43_phy *phy = &dev->phy;
- u32 hf;
+ u64 hf;
u16 tmp;
int autodiv = 0;
for (i = 0; i < 64; i++) {
tmp = b43_nrssi_hw_read(dev, i);
tmp -= val;
- tmp = limit_value(tmp, -32, 31);
+ tmp = clamp_val(tmp, -32, 31);
b43_nrssi_hw_write(dev, i, tmp);
}
}
tmp = (i - delta) * phy->nrssislope;
tmp /= 0x10000;
tmp += 0x3A;
- tmp = limit_value(tmp, 0, 0x3F);
+ tmp = clamp_val(tmp, 0, 0x3F);
phy->nrssi_lt[i] = tmp;
}
}
} else
threshold = phy->nrssi[1] - 5;
- threshold = limit_value(threshold, 0, 0x3E);
+ threshold = clamp_val(threshold, 0, 0x3E);
b43_phy_read(dev, 0x0020); /* dummy read */
b43_phy_write(dev, 0x0020,
(((u16) threshold) << 8) | 0x001C);
else
a += 32;
a = a >> 6;
- a = limit_value(a, -31, 31);
+ a = clamp_val(a, -31, 31);
b = b * (phy->nrssi[1] - phy->nrssi[0]);
b += (phy->nrssi[0] << 6);
else
b += 32;
b = b >> 6;
- b = limit_value(b, -31, 31);
+ b = clamp_val(b, -31, 31);
tmp_u16 = b43_phy_read(dev, 0x048A) & 0xF000;
tmp_u16 |= ((u32) b & 0x0000003F);
}
radio_stacksave(0x0078);
tmp = (b43_radio_read16(dev, 0x0078) & 0x001E);
- flipped = flip_4bit(tmp);
+ B43_WARN_ON(tmp > 15);
+ flipped = bitrev4(tmp);
if (flipped < 10 && flipped >= 8)
flipped = 7;
else if (flipped >= 10)
flipped -= 3;
- flipped = flip_4bit(flipped);
- flipped = (flipped << 1) | 0x0020;
+ flipped = (bitrev4(flipped) << 1) | 0x0020;
b43_radio_write16(dev, 0x0078, flipped);
b43_calc_nrssi_threshold(dev);
tmp1 >>= 9;
for (i = 0; i < 16; i++) {
- radio78 = ((flip_4bit(i) << 1) | 0x20);
+ radio78 = (bitrev4(i) << 1) | 0x0020;
b43_radio_write16(dev, 0x78, radio78);
udelay(10);
for (j = 0; j < 16; j++) {
void b43_set_rx_antenna(struct b43_wldev *dev, int antenna);
void b43_phy_xmitpower(struct b43_wldev *dev);
-void b43_gphy_dc_lt_init(struct b43_wldev *dev);
/* Returns the boolean whether the board has HardwarePowerControl */
bool b43_has_hardware_pctl(struct b43_phy *phy);
u8 max_val;
};
+/* Returns true, if the values are the same. */
+static inline bool b43_compare_rfatt(const struct b43_rfatt *a,
+ const struct b43_rfatt *b)
+{
+ return ((a->att == b->att) &&
+ (a->with_padmix == b->with_padmix));
+}
+
/* Baseband Attenuation */
struct b43_bbatt {
u8 att; /* Attenuation value */
u8 max_val;
};
+/* Returns true, if the values are the same. */
+static inline bool b43_compare_bbatt(const struct b43_bbatt *a,
+ const struct b43_bbatt *b)
+{
+ return (a->att == b->att);
+}
+
/* tx_control bits. */
#define B43_TXCTL_PA3DB 0x40 /* PA Gain 3dB */
#define B43_TXCTL_PA2DB 0x20 /* PA Gain 2dB */
--- /dev/null
+/*
+
+ Broadcom B43 wireless driver
+
+ PIO data transfer
+
+ Copyright (c) 2005-2008 Michael Buesch <mb@bu3sch.de>
+
+ 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 "pio.h"
+#include "dma.h"
+#include "main.h"
+#include "xmit.h"
+
+#include <linux/delay.h>
+
+
+static void b43_pio_rx_work(struct work_struct *work);
+
+
+static u16 generate_cookie(struct b43_pio_txqueue *q,
+ struct b43_pio_txpacket *pack)
+{
+ u16 cookie;
+
+ /* Use the upper 4 bits of the cookie as
+ * PIO controller ID and store the packet index number
+ * in the lower 12 bits.
+ * Note that the cookie must never be 0, as this
+ * is a special value used in RX path.
+ * It can also not be 0xFFFF because that is special
+ * for multicast frames.
+ */
+ cookie = (((u16)q->index + 1) << 12);
+ cookie |= pack->index;
+
+ return cookie;
+}
+
+static
+struct b43_pio_txqueue * parse_cookie(struct b43_wldev *dev,
+ u16 cookie,
+ struct b43_pio_txpacket **pack)
+{
+ struct b43_pio *pio = &dev->pio;
+ struct b43_pio_txqueue *q = NULL;
+ unsigned int pack_index;
+
+ switch (cookie & 0xF000) {
+ case 0x1000:
+ q = pio->tx_queue_AC_BK;
+ break;
+ case 0x2000:
+ q = pio->tx_queue_AC_BE;
+ break;
+ case 0x3000:
+ q = pio->tx_queue_AC_VI;
+ break;
+ case 0x4000:
+ q = pio->tx_queue_AC_VO;
+ break;
+ case 0x5000:
+ q = pio->tx_queue_mcast;
+ break;
+ }
+ if (B43_WARN_ON(!q))
+ return NULL;
+ pack_index = (cookie & 0x0FFF);
+ if (B43_WARN_ON(pack_index >= ARRAY_SIZE(q->packets)))
+ return NULL;
+ *pack = &q->packets[pack_index];
+
+ return q;
+}
+
+static u16 index_to_pioqueue_base(struct b43_wldev *dev,
+ unsigned int index)
+{
+ static const u16 bases[] = {
+ B43_MMIO_PIO_BASE0,
+ B43_MMIO_PIO_BASE1,
+ B43_MMIO_PIO_BASE2,
+ B43_MMIO_PIO_BASE3,
+ B43_MMIO_PIO_BASE4,
+ B43_MMIO_PIO_BASE5,
+ B43_MMIO_PIO_BASE6,
+ B43_MMIO_PIO_BASE7,
+ };
+ static const u16 bases_rev11[] = {
+ B43_MMIO_PIO11_BASE0,
+ B43_MMIO_PIO11_BASE1,
+ B43_MMIO_PIO11_BASE2,
+ B43_MMIO_PIO11_BASE3,
+ B43_MMIO_PIO11_BASE4,
+ B43_MMIO_PIO11_BASE5,
+ };
+
+ if (dev->dev->id.revision >= 11) {
+ B43_WARN_ON(index >= ARRAY_SIZE(bases_rev11));
+ return bases_rev11[index];
+ }
+ B43_WARN_ON(index >= ARRAY_SIZE(bases));
+ return bases[index];
+}
+
+static u16 pio_txqueue_offset(struct b43_wldev *dev)
+{
+ if (dev->dev->id.revision >= 11)
+ return 0x18;
+ return 0;
+}
+
+static u16 pio_rxqueue_offset(struct b43_wldev *dev)
+{
+ if (dev->dev->id.revision >= 11)
+ return 0x38;
+ return 8;
+}
+
+static struct b43_pio_txqueue * b43_setup_pioqueue_tx(struct b43_wldev *dev,
+ unsigned int index)
+{
+ struct b43_pio_txqueue *q;
+ struct b43_pio_txpacket *p;
+ unsigned int i;
+
+ q = kzalloc(sizeof(*q), GFP_KERNEL);
+ if (!q)
+ return NULL;
+ spin_lock_init(&q->lock);
+ q->dev = dev;
+ q->rev = dev->dev->id.revision;
+ q->mmio_base = index_to_pioqueue_base(dev, index) +
+ pio_txqueue_offset(dev);
+ q->index = index;
+
+ q->free_packet_slots = B43_PIO_MAX_NR_TXPACKETS;
+ if (q->rev >= 8) {
+ q->buffer_size = 1920; //FIXME this constant is wrong.
+ } else {
+ q->buffer_size = b43_piotx_read16(q, B43_PIO_TXQBUFSIZE);
+ q->buffer_size -= 80;
+ }
+
+ INIT_LIST_HEAD(&q->packets_list);
+ for (i = 0; i < ARRAY_SIZE(q->packets); i++) {
+ p = &(q->packets[i]);
+ INIT_LIST_HEAD(&p->list);
+ p->index = i;
+ p->queue = q;
+ list_add(&p->list, &q->packets_list);
+ }
+
+ return q;
+}
+
+static struct b43_pio_rxqueue * b43_setup_pioqueue_rx(struct b43_wldev *dev,
+ unsigned int index)
+{
+ struct b43_pio_rxqueue *q;
+
+ q = kzalloc(sizeof(*q), GFP_KERNEL);
+ if (!q)
+ return NULL;
+ spin_lock_init(&q->lock);
+ q->dev = dev;
+ q->rev = dev->dev->id.revision;
+ q->mmio_base = index_to_pioqueue_base(dev, index) +
+ pio_rxqueue_offset(dev);
+ INIT_WORK(&q->rx_work, b43_pio_rx_work);
+
+ /* Enable Direct FIFO RX (PIO) on the engine. */
+ b43_dma_direct_fifo_rx(dev, index, 1);
+
+ return q;
+}
+
+static void b43_pio_cancel_tx_packets(struct b43_pio_txqueue *q)
+{
+ struct b43_pio_txpacket *pack;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(q->packets); i++) {
+ pack = &(q->packets[i]);
+ if (pack->skb) {
+ dev_kfree_skb_any(pack->skb);
+ pack->skb = NULL;
+ }
+ }
+}
+
+static void b43_destroy_pioqueue_tx(struct b43_pio_txqueue *q,
+ const char *name)
+{
+ if (!q)
+ return;
+ b43_pio_cancel_tx_packets(q);
+ kfree(q);
+}
+
+static void b43_destroy_pioqueue_rx(struct b43_pio_rxqueue *q,
+ const char *name)
+{
+ if (!q)
+ return;
+ kfree(q);
+}
+
+#define destroy_queue_tx(pio, queue) do { \
+ b43_destroy_pioqueue_tx((pio)->queue, __stringify(queue)); \
+ (pio)->queue = NULL; \
+ } while (0)
+
+#define destroy_queue_rx(pio, queue) do { \
+ b43_destroy_pioqueue_rx((pio)->queue, __stringify(queue)); \
+ (pio)->queue = NULL; \
+ } while (0)
+
+void b43_pio_free(struct b43_wldev *dev)
+{
+ struct b43_pio *pio;
+
+ if (!b43_using_pio_transfers(dev))
+ return;
+ pio = &dev->pio;
+
+ destroy_queue_rx(pio, rx_queue);
+ destroy_queue_tx(pio, tx_queue_mcast);
+ destroy_queue_tx(pio, tx_queue_AC_VO);
+ destroy_queue_tx(pio, tx_queue_AC_VI);
+ destroy_queue_tx(pio, tx_queue_AC_BE);
+ destroy_queue_tx(pio, tx_queue_AC_BK);
+}
+
+void b43_pio_stop(struct b43_wldev *dev)
+{
+ if (!b43_using_pio_transfers(dev))
+ return;
+ cancel_work_sync(&dev->pio.rx_queue->rx_work);
+}
+
+int b43_pio_init(struct b43_wldev *dev)
+{
+ struct b43_pio *pio = &dev->pio;
+ int err = -ENOMEM;
+
+ b43_write32(dev, B43_MMIO_MACCTL, b43_read32(dev, B43_MMIO_MACCTL)
+ & ~B43_MACCTL_BE);
+ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_RXPADOFF, 0);
+
+ pio->tx_queue_AC_BK = b43_setup_pioqueue_tx(dev, 0);
+ if (!pio->tx_queue_AC_BK)
+ goto out;
+
+ pio->tx_queue_AC_BE = b43_setup_pioqueue_tx(dev, 1);
+ if (!pio->tx_queue_AC_BE)
+ goto err_destroy_bk;
+
+ pio->tx_queue_AC_VI = b43_setup_pioqueue_tx(dev, 2);
+ if (!pio->tx_queue_AC_VI)
+ goto err_destroy_be;
+
+ pio->tx_queue_AC_VO = b43_setup_pioqueue_tx(dev, 3);
+ if (!pio->tx_queue_AC_VO)
+ goto err_destroy_vi;
+
+ pio->tx_queue_mcast = b43_setup_pioqueue_tx(dev, 4);
+ if (!pio->tx_queue_mcast)
+ goto err_destroy_vo;
+
+ pio->rx_queue = b43_setup_pioqueue_rx(dev, 0);
+ if (!pio->rx_queue)
+ goto err_destroy_mcast;
+
+ b43dbg(dev->wl, "PIO initialized\n");
+ err = 0;
+out:
+ return err;
+
+err_destroy_mcast:
+ destroy_queue_tx(pio, tx_queue_mcast);
+err_destroy_vo:
+ destroy_queue_tx(pio, tx_queue_AC_VO);
+err_destroy_vi:
+ destroy_queue_tx(pio, tx_queue_AC_VI);
+err_destroy_be:
+ destroy_queue_tx(pio, tx_queue_AC_BE);
+err_destroy_bk:
+ destroy_queue_tx(pio, tx_queue_AC_BK);
+ return err;
+}
+
+/* Static mapping of mac80211's queues (priorities) to b43 PIO queues. */
+static struct b43_pio_txqueue * select_queue_by_priority(struct b43_wldev *dev,
+ u8 queue_prio)
+{
+ struct b43_pio_txqueue *q;
+
+ if (b43_modparam_qos) {
+ /* 0 = highest priority */
+ switch (queue_prio) {
+ default:
+ B43_WARN_ON(1);
+ /* fallthrough */
+ case 0:
+ q = dev->pio.tx_queue_AC_VO;
+ break;
+ case 1:
+ q = dev->pio.tx_queue_AC_VI;
+ break;
+ case 2:
+ q = dev->pio.tx_queue_AC_BE;
+ break;
+ case 3:
+ q = dev->pio.tx_queue_AC_BK;
+ break;
+ }
+ } else
+ q = dev->pio.tx_queue_AC_BE;
+
+ return q;
+}
+
+static u16 tx_write_2byte_queue(struct b43_pio_txqueue *q,
+ u16 ctl,
+ const void *_data,
+ unsigned int data_len)
+{
+ struct b43_wldev *dev = q->dev;
+ const u8 *data = _data;
+
+ ctl |= B43_PIO_TXCTL_WRITELO | B43_PIO_TXCTL_WRITEHI;
+ b43_piotx_write16(q, B43_PIO_TXCTL, ctl);
+
+ ssb_block_write(dev->dev, data, (data_len & ~1),
+ q->mmio_base + B43_PIO_TXDATA,
+ sizeof(u16));
+ if (data_len & 1) {
+ /* Write the last byte. */
+ ctl &= ~B43_PIO_TXCTL_WRITEHI;
+ b43_piotx_write16(q, B43_PIO_TXCTL, ctl);
+ b43_piotx_write16(q, B43_PIO_TXDATA, data[data_len - 1]);
+ }
+
+ return ctl;
+}
+
+static void pio_tx_frame_2byte_queue(struct b43_pio_txpacket *pack,
+ const u8 *hdr, unsigned int hdrlen)
+{
+ struct b43_pio_txqueue *q = pack->queue;
+ const char *frame = pack->skb->data;
+ unsigned int frame_len = pack->skb->len;
+ u16 ctl;
+
+ ctl = b43_piotx_read16(q, B43_PIO_TXCTL);
+ ctl |= B43_PIO_TXCTL_FREADY;
+ ctl &= ~B43_PIO_TXCTL_EOF;
+
+ /* Transfer the header data. */
+ ctl = tx_write_2byte_queue(q, ctl, hdr, hdrlen);
+ /* Transfer the frame data. */
+ ctl = tx_write_2byte_queue(q, ctl, frame, frame_len);
+
+ ctl |= B43_PIO_TXCTL_EOF;
+ b43_piotx_write16(q, B43_PIO_TXCTL, ctl);
+}
+
+static u32 tx_write_4byte_queue(struct b43_pio_txqueue *q,
+ u32 ctl,
+ const void *_data,
+ unsigned int data_len)
+{
+ struct b43_wldev *dev = q->dev;
+ const u8 *data = _data;
+
+ ctl |= B43_PIO8_TXCTL_0_7 | B43_PIO8_TXCTL_8_15 |
+ B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_24_31;
+ b43_piotx_write32(q, B43_PIO8_TXCTL, ctl);
+
+ ssb_block_write(dev->dev, data, (data_len & ~3),
+ q->mmio_base + B43_PIO8_TXDATA,
+ sizeof(u32));
+ if (data_len & 3) {
+ u32 value = 0;
+
+ /* Write the last few bytes. */
+ ctl &= ~(B43_PIO8_TXCTL_8_15 | B43_PIO8_TXCTL_16_23 |
+ B43_PIO8_TXCTL_24_31);
+ data = &(data[data_len - 1]);
+ switch (data_len & 3) {
+ case 3:
+ ctl |= B43_PIO8_TXCTL_16_23;
+ value |= (u32)(*data) << 16;
+ data--;
+ case 2:
+ ctl |= B43_PIO8_TXCTL_8_15;
+ value |= (u32)(*data) << 8;
+ data--;
+ case 1:
+ value |= (u32)(*data);
+ }
+ b43_piotx_write32(q, B43_PIO8_TXCTL, ctl);
+ b43_piotx_write32(q, B43_PIO8_TXDATA, value);
+ }
+
+ return ctl;
+}
+
+static void pio_tx_frame_4byte_queue(struct b43_pio_txpacket *pack,
+ const u8 *hdr, unsigned int hdrlen)
+{
+ struct b43_pio_txqueue *q = pack->queue;
+ const char *frame = pack->skb->data;
+ unsigned int frame_len = pack->skb->len;
+ u32 ctl;
+
+ ctl = b43_piotx_read32(q, B43_PIO8_TXCTL);
+ ctl |= B43_PIO8_TXCTL_FREADY;
+ ctl &= ~B43_PIO8_TXCTL_EOF;
+
+ /* Transfer the header data. */
+ ctl = tx_write_4byte_queue(q, ctl, hdr, hdrlen);
+ /* Transfer the frame data. */
+ ctl = tx_write_4byte_queue(q, ctl, frame, frame_len);
+
+ ctl |= B43_PIO8_TXCTL_EOF;
+ b43_piotx_write32(q, B43_PIO_TXCTL, ctl);
+}
+
+static int pio_tx_frame(struct b43_pio_txqueue *q,
+ struct sk_buff *skb)
+{
+ struct b43_pio_txpacket *pack;
+ struct b43_txhdr txhdr;
+ u16 cookie;
+ int err;
+ unsigned int hdrlen;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ B43_WARN_ON(list_empty(&q->packets_list));
+ pack = list_entry(q->packets_list.next,
+ struct b43_pio_txpacket, list);
+
+ cookie = generate_cookie(q, pack);
+ hdrlen = b43_txhdr_size(q->dev);
+ err = b43_generate_txhdr(q->dev, (u8 *)&txhdr, skb->data,
+ skb->len, info, cookie);
+ if (err)
+ return err;
+
+ if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
+ /* Tell the firmware about the cookie of the last
+ * mcast frame, so it can clear the more-data bit in it. */
+ b43_shm_write16(q->dev, B43_SHM_SHARED,
+ B43_SHM_SH_MCASTCOOKIE, cookie);
+ }
+
+ pack->skb = skb;
+ if (q->rev >= 8)
+ pio_tx_frame_4byte_queue(pack, (const u8 *)&txhdr, hdrlen);
+ else
+ pio_tx_frame_2byte_queue(pack, (const u8 *)&txhdr, hdrlen);
+
+ /* Remove it from the list of available packet slots.
+ * It will be put back when we receive the status report. */
+ list_del(&pack->list);
+
+ /* Update the queue statistics. */
+ q->buffer_used += roundup(skb->len + hdrlen, 4);
+ q->free_packet_slots -= 1;
+
+ return 0;
+}
+
+int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb)
+{
+ struct b43_pio_txqueue *q;
+ struct ieee80211_hdr *hdr;
+ unsigned long flags;
+ unsigned int hdrlen, total_len;
+ int err = 0;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
+ /* The multicast queue will be sent after the DTIM. */
+ q = dev->pio.tx_queue_mcast;
+ /* Set the frame More-Data bit. Ucode will clear it
+ * for us on the last frame. */
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ } else {
+ /* Decide by priority where to put this frame. */
+ q = select_queue_by_priority(dev, skb_get_queue_mapping(skb));
+ }
+
+ spin_lock_irqsave(&q->lock, flags);
+
+ hdrlen = b43_txhdr_size(dev);
+ total_len = roundup(skb->len + hdrlen, 4);
+
+ if (unlikely(total_len > q->buffer_size)) {
+ err = -ENOBUFS;
+ b43dbg(dev->wl, "PIO: TX packet longer than queue.\n");
+ goto out_unlock;
+ }
+ if (unlikely(q->free_packet_slots == 0)) {
+ err = -ENOBUFS;
+ b43warn(dev->wl, "PIO: TX packet overflow.\n");
+ goto out_unlock;
+ }
+ B43_WARN_ON(q->buffer_used > q->buffer_size);
+
+ if (total_len > (q->buffer_size - q->buffer_used)) {
+ /* Not enough memory on the queue. */
+ err = -EBUSY;
+ ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
+ q->stopped = 1;
+ goto out_unlock;
+ }
+
+ /* Assign the queue number to the ring (if not already done before)
+ * so TX status handling can use it. The mac80211-queue to b43-queue
+ * mapping is static, so we don't need to store it per frame. */
+ q->queue_prio = skb_get_queue_mapping(skb);
+
+ err = pio_tx_frame(q, skb);
+ if (unlikely(err == -ENOKEY)) {
+ /* Drop this packet, as we don't have the encryption key
+ * anymore and must not transmit it unencrypted. */
+ dev_kfree_skb_any(skb);
+ err = 0;
+ goto out_unlock;
+ }
+ if (unlikely(err)) {
+ b43err(dev->wl, "PIO transmission failure\n");
+ goto out_unlock;
+ }
+ q->nr_tx_packets++;
+
+ B43_WARN_ON(q->buffer_used > q->buffer_size);
+ if (((q->buffer_size - q->buffer_used) < roundup(2 + 2 + 6, 4)) ||
+ (q->free_packet_slots == 0)) {
+ /* The queue is full. */
+ ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
+ q->stopped = 1;
+ }
+
+out_unlock:
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ return err;
+}
+
+/* Called with IRQs disabled. */
+void b43_pio_handle_txstatus(struct b43_wldev *dev,
+ const struct b43_txstatus *status)
+{
+ struct b43_pio_txqueue *q;
+ struct b43_pio_txpacket *pack = NULL;
+ unsigned int total_len;
+ struct ieee80211_tx_info *info;
+
+ q = parse_cookie(dev, status->cookie, &pack);
+ if (unlikely(!q))
+ return;
+ B43_WARN_ON(!pack);
+
+ spin_lock(&q->lock); /* IRQs are already disabled. */
+
+ info = (void *)pack->skb;
+ memset(&info->status, 0, sizeof(info->status));
+
+ b43_fill_txstatus_report(info, status);
+
+ total_len = pack->skb->len + b43_txhdr_size(dev);
+ total_len = roundup(total_len, 4);
+ q->buffer_used -= total_len;
+ q->free_packet_slots += 1;
+
+ ieee80211_tx_status_irqsafe(dev->wl->hw, pack->skb);
+ pack->skb = NULL;
+ list_add(&pack->list, &q->packets_list);
+
+ if (q->stopped) {
+ ieee80211_wake_queue(dev->wl->hw, q->queue_prio);
+ q->stopped = 0;
+ }
+
+ spin_unlock(&q->lock);
+}
+
+void b43_pio_get_tx_stats(struct b43_wldev *dev,
+ struct ieee80211_tx_queue_stats *stats)
+{
+ const int nr_queues = dev->wl->hw->queues;
+ struct b43_pio_txqueue *q;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < nr_queues; i++) {
+ q = select_queue_by_priority(dev, i);
+
+ spin_lock_irqsave(&q->lock, flags);
+ stats[i].len = B43_PIO_MAX_NR_TXPACKETS - q->free_packet_slots;
+ stats[i].limit = B43_PIO_MAX_NR_TXPACKETS;
+ stats[i].count = q->nr_tx_packets;
+ spin_unlock_irqrestore(&q->lock, flags);
+ }
+}
+
+/* Returns whether we should fetch another frame. */
+static bool pio_rx_frame(struct b43_pio_rxqueue *q)
+{
+ struct b43_wldev *dev = q->dev;
+ struct b43_rxhdr_fw4 rxhdr;
+ u16 len;
+ u32 macstat;
+ unsigned int i, padding;
+ struct sk_buff *skb;
+ const char *err_msg = NULL;
+
+ memset(&rxhdr, 0, sizeof(rxhdr));
+
+ /* Check if we have data and wait for it to get ready. */
+ if (q->rev >= 8) {
+ u32 ctl;
+
+ ctl = b43_piorx_read32(q, B43_PIO8_RXCTL);
+ if (!(ctl & B43_PIO8_RXCTL_FRAMERDY))
+ return 0;
+ b43_piorx_write32(q, B43_PIO8_RXCTL,
+ B43_PIO8_RXCTL_FRAMERDY);
+ for (i = 0; i < 10; i++) {
+ ctl = b43_piorx_read32(q, B43_PIO8_RXCTL);
+ if (ctl & B43_PIO8_RXCTL_DATARDY)
+ goto data_ready;
+ udelay(10);
+ }
+ } else {
+ u16 ctl;
+
+ ctl = b43_piorx_read16(q, B43_PIO_RXCTL);
+ if (!(ctl & B43_PIO_RXCTL_FRAMERDY))
+ return 0;
+ b43_piorx_write16(q, B43_PIO_RXCTL,
+ B43_PIO_RXCTL_FRAMERDY);
+ for (i = 0; i < 10; i++) {
+ ctl = b43_piorx_read16(q, B43_PIO_RXCTL);
+ if (ctl & B43_PIO_RXCTL_DATARDY)
+ goto data_ready;
+ udelay(10);
+ }
+ }
+ b43dbg(q->dev->wl, "PIO RX timed out\n");
+ return 1;
+data_ready:
+
+ /* Get the preamble (RX header) */
+ if (q->rev >= 8) {
+ ssb_block_read(dev->dev, &rxhdr, sizeof(rxhdr),
+ q->mmio_base + B43_PIO8_RXDATA,
+ sizeof(u32));
+ } else {
+ ssb_block_read(dev->dev, &rxhdr, sizeof(rxhdr),
+ q->mmio_base + B43_PIO_RXDATA,
+ sizeof(u16));
+ }
+ /* Sanity checks. */
+ len = le16_to_cpu(rxhdr.frame_len);
+ if (unlikely(len > 0x700)) {
+ err_msg = "len > 0x700";
+ goto rx_error;
+ }
+ if (unlikely(len == 0)) {
+ err_msg = "len == 0";
+ goto rx_error;
+ }
+
+ macstat = le32_to_cpu(rxhdr.mac_status);
+ if (macstat & B43_RX_MAC_FCSERR) {
+ if (!(q->dev->wl->filter_flags & FIF_FCSFAIL)) {
+ /* Drop frames with failed FCS. */
+ err_msg = "Frame FCS error";
+ goto rx_error;
+ }
+ }
+
+ /* We always pad 2 bytes, as that's what upstream code expects
+ * due to the RX-header being 30 bytes. In case the frame is
+ * unaligned, we pad another 2 bytes. */
+ padding = (macstat & B43_RX_MAC_PADDING) ? 2 : 0;
+ skb = dev_alloc_skb(len + padding + 2);
+ if (unlikely(!skb)) {
+ err_msg = "Out of memory";
+ goto rx_error;
+ }
+ skb_reserve(skb, 2);
+ skb_put(skb, len + padding);
+ if (q->rev >= 8) {
+ ssb_block_read(dev->dev, skb->data + padding, (len & ~3),
+ q->mmio_base + B43_PIO8_RXDATA,
+ sizeof(u32));
+ if (len & 3) {
+ u32 value;
+ char *data;
+
+ /* Read the last few bytes. */
+ value = b43_piorx_read32(q, B43_PIO8_RXDATA);
+ data = &(skb->data[len + padding - 1]);
+ switch (len & 3) {
+ case 3:
+ *data = (value >> 16);
+ data--;
+ case 2:
+ *data = (value >> 8);
+ data--;
+ case 1:
+ *data = value;
+ }
+ }
+ } else {
+ ssb_block_read(dev->dev, skb->data + padding, (len & ~1),
+ q->mmio_base + B43_PIO_RXDATA,
+ sizeof(u16));
+ if (len & 1) {
+ u16 value;
+
+ /* Read the last byte. */
+ value = b43_piorx_read16(q, B43_PIO_RXDATA);
+ skb->data[len + padding - 1] = value;
+ }
+ }
+
+ b43_rx(q->dev, skb, &rxhdr);
+
+ return 1;
+
+rx_error:
+ if (err_msg)
+ b43dbg(q->dev->wl, "PIO RX error: %s\n", err_msg);
+ b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_DATARDY);
+ return 1;
+}
+
+/* RX workqueue. We can sleep, yay! */
+static void b43_pio_rx_work(struct work_struct *work)
+{
+ struct b43_pio_rxqueue *q = container_of(work, struct b43_pio_rxqueue,
+ rx_work);
+ unsigned int budget = 50;
+ bool stop;
+
+ do {
+ spin_lock_irq(&q->lock);
+ stop = (pio_rx_frame(q) == 0);
+ spin_unlock_irq(&q->lock);
+ cond_resched();
+ if (stop)
+ break;
+ } while (--budget);
+}
+
+/* Called with IRQs disabled. */
+void b43_pio_rx(struct b43_pio_rxqueue *q)
+{
+ /* Due to latency issues we must run the RX path in
+ * a workqueue to be able to schedule between packets. */
+ queue_work(q->dev->wl->hw->workqueue, &q->rx_work);
+}
+
+static void b43_pio_tx_suspend_queue(struct b43_pio_txqueue *q)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&q->lock, flags);
+ if (q->rev >= 8) {
+ b43_piotx_write32(q, B43_PIO8_TXCTL,
+ b43_piotx_read32(q, B43_PIO8_TXCTL)
+ | B43_PIO8_TXCTL_SUSPREQ);
+ } else {
+ b43_piotx_write16(q, B43_PIO_TXCTL,
+ b43_piotx_read16(q, B43_PIO_TXCTL)
+ | B43_PIO_TXCTL_SUSPREQ);
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+}
+
+static void b43_pio_tx_resume_queue(struct b43_pio_txqueue *q)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&q->lock, flags);
+ if (q->rev >= 8) {
+ b43_piotx_write32(q, B43_PIO8_TXCTL,
+ b43_piotx_read32(q, B43_PIO8_TXCTL)
+ & ~B43_PIO8_TXCTL_SUSPREQ);
+ } else {
+ b43_piotx_write16(q, B43_PIO_TXCTL,
+ b43_piotx_read16(q, B43_PIO_TXCTL)
+ & ~B43_PIO_TXCTL_SUSPREQ);
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+}
+
+void b43_pio_tx_suspend(struct b43_wldev *dev)
+{
+ b43_power_saving_ctl_bits(dev, B43_PS_AWAKE);
+ b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_BK);
+ b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_BE);
+ b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_VI);
+ b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_VO);
+ b43_pio_tx_suspend_queue(dev->pio.tx_queue_mcast);
+}
+
+void b43_pio_tx_resume(struct b43_wldev *dev)
+{
+ b43_pio_tx_resume_queue(dev->pio.tx_queue_mcast);
+ b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_VO);
+ b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_VI);
+ b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_BE);
+ b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_BK);
+ b43_power_saving_ctl_bits(dev, 0);
+}
--- /dev/null
+#ifndef B43_PIO_H_
+#define B43_PIO_H_
+
+#include "b43.h"
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+
+
+/*** Registers for PIO queues up to revision 7. ***/
+/* TX queue. */
+#define B43_PIO_TXCTL 0x00
+#define B43_PIO_TXCTL_WRITELO 0x0001
+#define B43_PIO_TXCTL_WRITEHI 0x0002
+#define B43_PIO_TXCTL_EOF 0x0004
+#define B43_PIO_TXCTL_FREADY 0x0008
+#define B43_PIO_TXCTL_FLUSHREQ 0x0020
+#define B43_PIO_TXCTL_FLUSHPEND 0x0040
+#define B43_PIO_TXCTL_SUSPREQ 0x0080
+#define B43_PIO_TXCTL_QSUSP 0x0100
+#define B43_PIO_TXCTL_COMMCNT 0xFC00
+#define B43_PIO_TXCTL_COMMCNT_SHIFT 10
+#define B43_PIO_TXDATA 0x02
+#define B43_PIO_TXQBUFSIZE 0x04
+/* RX queue. */
+#define B43_PIO_RXCTL 0x00
+#define B43_PIO_RXCTL_FRAMERDY 0x0001
+#define B43_PIO_RXCTL_DATARDY 0x0002
+#define B43_PIO_RXDATA 0x02
+
+/*** Registers for PIO queues revision 8 and later. ***/
+/* TX queue */
+#define B43_PIO8_TXCTL 0x00
+#define B43_PIO8_TXCTL_0_7 0x00000001
+#define B43_PIO8_TXCTL_8_15 0x00000002
+#define B43_PIO8_TXCTL_16_23 0x00000004
+#define B43_PIO8_TXCTL_24_31 0x00000008
+#define B43_PIO8_TXCTL_EOF 0x00000010
+#define B43_PIO8_TXCTL_FREADY 0x00000080
+#define B43_PIO8_TXCTL_SUSPREQ 0x00000100
+#define B43_PIO8_TXCTL_QSUSP 0x00000200
+#define B43_PIO8_TXCTL_FLUSHREQ 0x00000400
+#define B43_PIO8_TXCTL_FLUSHPEND 0x00000800
+#define B43_PIO8_TXDATA 0x04
+/* RX queue */
+#define B43_PIO8_RXCTL 0x00
+#define B43_PIO8_RXCTL_FRAMERDY 0x00000001
+#define B43_PIO8_RXCTL_DATARDY 0x00000002
+#define B43_PIO8_RXDATA 0x04
+
+
+/* The maximum number of TX-packets the HW can handle. */
+#define B43_PIO_MAX_NR_TXPACKETS 32
+
+
+#ifdef CONFIG_B43_PIO
+
+struct b43_pio_txpacket {
+ /* Pointer to the TX queue we belong to. */
+ struct b43_pio_txqueue *queue;
+ /* The TX data packet. */
+ struct sk_buff *skb;
+ /* Index in the (struct b43_pio_txqueue)->packets array. */
+ u8 index;
+
+ struct list_head list;
+};
+
+struct b43_pio_txqueue {
+ struct b43_wldev *dev;
+ spinlock_t lock;
+ u16 mmio_base;
+
+ /* The device queue buffer size in bytes. */
+ u16 buffer_size;
+ /* The number of used bytes in the device queue buffer. */
+ u16 buffer_used;
+ /* The number of packets that can still get queued.
+ * This is decremented on queueing a packet and incremented
+ * after receiving the transmit status. */
+ u16 free_packet_slots;
+
+ /* True, if the mac80211 queue was stopped due to overflow at TX. */
+ bool stopped;
+ /* Our b43 queue index number */
+ u8 index;
+ /* The mac80211 QoS queue priority. */
+ u8 queue_prio;
+
+ /* Buffer for TX packet meta data. */
+ struct b43_pio_txpacket packets[B43_PIO_MAX_NR_TXPACKETS];
+ struct list_head packets_list;
+
+ /* Total number of transmitted packets. */
+ unsigned int nr_tx_packets;
+
+ /* Shortcut to the 802.11 core revision. This is to
+ * avoid horrible pointer dereferencing in the fastpaths. */
+ u8 rev;
+};
+
+struct b43_pio_rxqueue {
+ struct b43_wldev *dev;
+ spinlock_t lock;
+ u16 mmio_base;
+
+ /* Work to reduce latency issues on RX. */
+ struct work_struct rx_work;
+
+ /* Shortcut to the 802.11 core revision. This is to
+ * avoid horrible pointer dereferencing in the fastpaths. */
+ u8 rev;
+};
+
+
+static inline u16 b43_piotx_read16(struct b43_pio_txqueue *q, u16 offset)
+{
+ return b43_read16(q->dev, q->mmio_base + offset);
+}
+
+static inline u32 b43_piotx_read32(struct b43_pio_txqueue *q, u16 offset)
+{
+ return b43_read32(q->dev, q->mmio_base + offset);
+}
+
+static inline void b43_piotx_write16(struct b43_pio_txqueue *q,
+ u16 offset, u16 value)
+{
+ b43_write16(q->dev, q->mmio_base + offset, value);
+}
+
+static inline void b43_piotx_write32(struct b43_pio_txqueue *q,
+ u16 offset, u32 value)
+{
+ b43_write32(q->dev, q->mmio_base + offset, value);
+}
+
+
+static inline u16 b43_piorx_read16(struct b43_pio_rxqueue *q, u16 offset)
+{
+ return b43_read16(q->dev, q->mmio_base + offset);
+}
+
+static inline u32 b43_piorx_read32(struct b43_pio_rxqueue *q, u16 offset)
+{
+ return b43_read32(q->dev, q->mmio_base + offset);
+}
+
+static inline void b43_piorx_write16(struct b43_pio_rxqueue *q,
+ u16 offset, u16 value)
+{
+ b43_write16(q->dev, q->mmio_base + offset, value);
+}
+
+static inline void b43_piorx_write32(struct b43_pio_rxqueue *q,
+ u16 offset, u32 value)
+{
+ b43_write32(q->dev, q->mmio_base + offset, value);
+}
+
+
+int b43_pio_init(struct b43_wldev *dev);
+void b43_pio_stop(struct b43_wldev *dev);
+void b43_pio_free(struct b43_wldev *dev);
+
+int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb);
+void b43_pio_handle_txstatus(struct b43_wldev *dev,
+ const struct b43_txstatus *status);
+void b43_pio_get_tx_stats(struct b43_wldev *dev,
+ struct ieee80211_tx_queue_stats *stats);
+void b43_pio_rx(struct b43_pio_rxqueue *q);
+
+void b43_pio_tx_suspend(struct b43_wldev *dev);
+void b43_pio_tx_resume(struct b43_wldev *dev);
+
+
+#else /* CONFIG_B43_PIO */
+
+
+static inline int b43_pio_init(struct b43_wldev *dev)
+{
+ return 0;
+}
+static inline void b43_pio_free(struct b43_wldev *dev)
+{
+}
+static inline void b43_pio_stop(struct b43_wldev *dev)
+{
+}
+static inline int b43_pio_tx(struct b43_wldev *dev,
+ struct sk_buff *skb)
+{
+ return 0;
+}
+static inline void b43_pio_handle_txstatus(struct b43_wldev *dev,
+ const struct b43_txstatus *status)
+{
+}
+static inline void b43_pio_get_tx_stats(struct b43_wldev *dev,
+ struct ieee80211_tx_queue_stats *stats)
+{
+}
+static inline void b43_pio_rx(struct b43_pio_rxqueue *q)
+{
+}
+static inline void b43_pio_tx_suspend(struct b43_wldev *dev)
+{
+}
+static inline void b43_pio_tx_resume(struct b43_wldev *dev)
+{
+}
+
+#endif /* CONFIG_B43_PIO */
+#endif /* B43_PIO_H_ */
b43_ofdmtab_write32(dev, B43_OFDMTAB_ROTOR, i, b43_tab_rotor[i]);
}
+static void b43_write_null_nst(struct b43_wldev *dev)
+{
+ int i;
+
+ for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
+ b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE, i, 0);
+}
+
+static void b43_write_nst(struct b43_wldev *dev, const u16 *nst)
+{
+ int i;
+
+ for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
+ b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE, i, nst[i]);
+}
+
static void b43_wa_nst(struct b43_wldev *dev) /* Noise scale table */
{
struct b43_phy *phy = &dev->phy;
- int i;
if (phy->type == B43_PHYTYPE_A) {
if (phy->rev <= 1)
- for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
- b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
- i, 0);
+ b43_write_null_nst(dev);
else if (phy->rev == 2)
- for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
- b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
- i, b43_tab_noisescalea2[i]);
+ b43_write_nst(dev, b43_tab_noisescalea2);
else if (phy->rev == 3)
- for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
- b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
- i, b43_tab_noisescalea3[i]);
+ b43_write_nst(dev, b43_tab_noisescalea3);
else
- for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
- b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
- i, b43_tab_noisescaleg3[i]);
+ b43_write_nst(dev, b43_tab_noisescaleg3);
} else {
if (phy->rev >= 6) {
if (b43_phy_read(dev, B43_PHY_ENCORE) & B43_PHY_ENCORE_EN)
- for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
- b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
- i, b43_tab_noisescaleg3[i]);
+ b43_write_nst(dev, b43_tab_noisescaleg3);
else
- for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
- b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
- i, b43_tab_noisescaleg2[i]);
+ b43_write_nst(dev, b43_tab_noisescaleg2);
} else {
- for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
- b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
- i, b43_tab_noisescaleg1[i]);
+ b43_write_nst(dev, b43_tab_noisescaleg1);
}
}
}
#include "xmit.h"
#include "phy.h"
#include "dma.h"
+#include "pio.h"
/* Extract the bitrate index out of a CCK PLCP header. */
u8 *_txhdr,
const unsigned char *fragment_data,
unsigned int fragment_len,
- const struct ieee80211_tx_control *txctl,
+ const struct ieee80211_tx_info *info,
u16 cookie)
{
struct b43_txhdr *txhdr = (struct b43_txhdr *)_txhdr;
const struct b43_phy *phy = &dev->phy;
const struct ieee80211_hdr *wlhdr =
(const struct ieee80211_hdr *)fragment_data;
- int use_encryption = (!(txctl->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT));
+ int use_encryption = (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT));
u16 fctl = le16_to_cpu(wlhdr->frame_control);
struct ieee80211_rate *fbrate;
u8 rate, rate_fb;
u32 mac_ctl = 0;
u16 phy_ctl = 0;
u8 extra_ft = 0;
+ struct ieee80211_rate *txrate;
memset(txhdr, 0, sizeof(*txhdr));
- WARN_ON(!txctl->tx_rate);
- rate = txctl->tx_rate ? txctl->tx_rate->hw_value : B43_CCK_RATE_1MB;
+ txrate = ieee80211_get_tx_rate(dev->wl->hw, info);
+ rate = txrate ? txrate->hw_value : B43_CCK_RATE_1MB;
rate_ofdm = b43_is_ofdm_rate(rate);
- fbrate = txctl->alt_retry_rate ? : txctl->tx_rate;
+ fbrate = ieee80211_get_alt_retry_rate(dev->wl->hw, info) ? : txrate;
rate_fb = fbrate->hw_value;
rate_fb_ofdm = b43_is_ofdm_rate(rate_fb);
* use the original dur_id field. */
txhdr->dur_fb = wlhdr->duration_id;
} else {
- txhdr->dur_fb = ieee80211_generic_frame_duration(dev->wl->hw,
- txctl->vif,
- fragment_len,
- fbrate);
+ txhdr->dur_fb = ieee80211_generic_frame_duration(
+ dev->wl->hw, info->control.vif, fragment_len, fbrate);
}
plcp_fragment_len = fragment_len + FCS_LEN;
if (use_encryption) {
- u8 key_idx = (u16) (txctl->key_idx);
+ u8 key_idx = info->control.hw_key->hw_key_idx;
struct b43_key *key;
int wlhdr_len;
size_t iv_len;
}
/* Hardware appends ICV. */
- plcp_fragment_len += txctl->icv_len;
+ plcp_fragment_len += info->control.icv_len;
key_idx = b43_kidx_to_fw(dev, key_idx);
mac_ctl |= (key_idx << B43_TXH_MAC_KEYIDX_SHIFT) &
mac_ctl |= (key->algorithm << B43_TXH_MAC_KEYALG_SHIFT) &
B43_TXH_MAC_KEYALG;
wlhdr_len = ieee80211_get_hdrlen(fctl);
- iv_len = min((size_t) txctl->iv_len,
+ iv_len = min((size_t) info->control.iv_len,
ARRAY_SIZE(txhdr->iv));
memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len);
}
phy_ctl |= B43_TXH_PHY_ENC_OFDM;
else
phy_ctl |= B43_TXH_PHY_ENC_CCK;
- if (txctl->flags & IEEE80211_TXCTL_SHORT_PREAMBLE)
+ if (info->flags & IEEE80211_TX_CTL_SHORT_PREAMBLE)
phy_ctl |= B43_TXH_PHY_SHORTPRMBL;
- switch (b43_ieee80211_antenna_sanitize(dev, txctl->antenna_sel_tx)) {
+ switch (b43_ieee80211_antenna_sanitize(dev, info->antenna_sel_tx)) {
case 0: /* Default */
phy_ctl |= B43_TXH_PHY_ANT01AUTO;
break;
}
/* MAC control */
- if (!(txctl->flags & IEEE80211_TXCTL_NO_ACK))
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
mac_ctl |= B43_TXH_MAC_ACK;
if (!(((fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) &&
((fctl & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)))
mac_ctl |= B43_TXH_MAC_HWSEQ;
- if (txctl->flags & IEEE80211_TXCTL_FIRST_FRAGMENT)
+ if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
mac_ctl |= B43_TXH_MAC_STMSDU;
if (phy->type == B43_PHYTYPE_A)
mac_ctl |= B43_TXH_MAC_5GHZ;
- if (txctl->flags & IEEE80211_TXCTL_LONG_RETRY_LIMIT)
+ if (info->flags & IEEE80211_TX_CTL_LONG_RETRY_LIMIT)
mac_ctl |= B43_TXH_MAC_LONGFRAME;
/* Generate the RTS or CTS-to-self frame */
- if ((txctl->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
- (txctl->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) {
+ if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
+ (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)) {
unsigned int len;
struct ieee80211_hdr *hdr;
int rts_rate, rts_rate_fb;
int rts_rate_ofdm, rts_rate_fb_ofdm;
struct b43_plcp_hdr6 *plcp;
+ struct ieee80211_rate *rts_cts_rate;
- WARN_ON(!txctl->rts_cts_rate);
- rts_rate = txctl->rts_cts_rate ? txctl->rts_cts_rate->hw_value : B43_CCK_RATE_1MB;
+ rts_cts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info);
+
+ rts_rate = rts_cts_rate ? rts_cts_rate->hw_value : B43_CCK_RATE_1MB;
rts_rate_ofdm = b43_is_ofdm_rate(rts_rate);
rts_rate_fb = b43_calc_fallback_rate(rts_rate);
rts_rate_fb_ofdm = b43_is_ofdm_rate(rts_rate_fb);
- if (txctl->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) {
+ if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) {
struct ieee80211_cts *cts;
if (b43_is_old_txhdr_format(dev)) {
cts = (struct ieee80211_cts *)
(txhdr->new_format.rts_frame);
}
- ieee80211_ctstoself_get(dev->wl->hw, txctl->vif,
+ ieee80211_ctstoself_get(dev->wl->hw, info->control.vif,
fragment_data, fragment_len,
- txctl, cts);
+ info, cts);
mac_ctl |= B43_TXH_MAC_SENDCTS;
len = sizeof(struct ieee80211_cts);
} else {
rts = (struct ieee80211_rts *)
(txhdr->new_format.rts_frame);
}
- ieee80211_rts_get(dev->wl->hw, txctl->vif,
+ ieee80211_rts_get(dev->wl->hw, info->control.vif,
fragment_data, fragment_len,
- txctl, rts);
+ info, rts);
mac_ctl |= B43_TXH_MAC_SENDRTS;
len = sizeof(struct ieee80211_rts);
}
u32 macstat;
u16 chanid;
u16 phytype;
- u8 jssi;
int padding;
memset(&status, 0, sizeof(status));
/* Get metadata about the frame from the header. */
phystat0 = le16_to_cpu(rxhdr->phy_status0);
phystat3 = le16_to_cpu(rxhdr->phy_status3);
- jssi = rxhdr->jssi;
macstat = le32_to_cpu(rxhdr->mac_status);
mactime = le16_to_cpu(rxhdr->mac_time);
chanstat = le16_to_cpu(rxhdr->channel);
}
}
- status.ssi = b43_rssi_postprocess(dev, jssi,
- (phystat0 & B43_RX_PHYST0_OFDM),
- (phystat0 & B43_RX_PHYST0_GAINCTL),
- (phystat3 & B43_RX_PHYST3_TRSTATE));
+ /* Link quality statistics */
status.noise = dev->stats.link_noise;
- /* the next line looks wrong, but is what mac80211 wants */
- status.signal = (jssi * 100) / B43_RX_MAX_SSI;
+ if ((chanstat & B43_RX_CHAN_PHYTYPE) == B43_PHYTYPE_N) {
+// s8 rssi = max(rxhdr->power0, rxhdr->power1);
+ //TODO: Find out what the rssi value is (dBm or percentage?)
+ // and also find out what the maximum possible value is.
+ // Fill status.ssi and status.signal fields.
+ } else {
+ status.signal = b43_rssi_postprocess(dev, rxhdr->jssi,
+ (phystat0 & B43_RX_PHYST0_OFDM),
+ (phystat0 & B43_RX_PHYST0_GAINCTL),
+ (phystat3 & B43_RX_PHYST3_TRSTATE));
+ status.qual = (rxhdr->jssi * 100) / B43_RX_MAX_SSI;
+ }
+
if (phystat0 & B43_RX_PHYST0_OFDM)
status.rate_idx = b43_plcp_get_bitrate_idx_ofdm(plcp,
phytype == B43_PHYTYPE_A);
status.antenna = !!(phystat0 & B43_RX_PHYST0_ANT);
/*
- * If monitors are present get full 64-bit timestamp. This
- * code assumes we get to process the packet within 16 bits
- * of timestamp, i.e. about 65 milliseconds after the PHY
- * received the first symbol.
+ * All frames on monitor interfaces and beacons always need a full
+ * 64-bit timestamp. Monitor interfaces need it for diagnostic
+ * purposes and beacons for IBSS merging.
+ * This code assumes we get to process the packet within 16 bits
+ * of timestamp, i.e. about 65 milliseconds after the PHY received
+ * the first symbol.
*/
- if (dev->wl->radiotap_enabled) {
+ if (((fctl & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE))
+ == (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON)) ||
+ dev->wl->radiotap_enabled) {
u16 low_mactime_now;
b43_tsf_read(dev, &status.mactime);
dev->wl->ieee_stats.dot11RTSSuccessCount++;
}
- b43_dma_handle_txstatus(dev, status);
+ if (b43_using_pio_transfers(dev))
+ b43_pio_handle_txstatus(dev, status);
+ else
+ b43_dma_handle_txstatus(dev, status);
}
-/* Handle TX status report as received through DMA/PIO queues */
-void b43_handle_hwtxstatus(struct b43_wldev *dev,
- const struct b43_hwtxstatus *hw)
+/* Fill out the mac80211 TXstatus report based on the b43-specific
+ * txstatus report data. This returns a boolean whether the frame was
+ * successfully transmitted. */
+bool b43_fill_txstatus_report(struct ieee80211_tx_info *report,
+ const struct b43_txstatus *status)
{
- struct b43_txstatus status;
- u8 tmp;
-
- status.cookie = le16_to_cpu(hw->cookie);
- status.seq = le16_to_cpu(hw->seq);
- status.phy_stat = hw->phy_stat;
- tmp = hw->count;
- status.frame_count = (tmp >> 4);
- status.rts_count = (tmp & 0x0F);
- tmp = hw->flags;
- status.supp_reason = ((tmp & 0x1C) >> 2);
- status.pm_indicated = !!(tmp & 0x80);
- status.intermediate = !!(tmp & 0x40);
- status.for_ampdu = !!(tmp & 0x20);
- status.acked = !!(tmp & 0x02);
-
- b43_handle_txstatus(dev, &status);
+ bool frame_success = 1;
+
+ if (status->acked) {
+ /* The frame was ACKed. */
+ report->flags |= IEEE80211_TX_STAT_ACK;
+ } else {
+ /* The frame was not ACKed... */
+ if (!(report->flags & IEEE80211_TX_CTL_NO_ACK)) {
+ /* ...but we expected an ACK. */
+ frame_success = 0;
+ report->status.excessive_retries = 1;
+ }
+ }
+ if (status->frame_count == 0) {
+ /* The frame was not transmitted at all. */
+ report->status.retry_count = 0;
+ } else
+ report->status.retry_count = status->frame_count - 1;
+
+ return frame_success;
}
/* Stop any TX operation on the device (suspend the hardware queues) */
void b43_tx_suspend(struct b43_wldev *dev)
{
- b43_dma_tx_suspend(dev);
+ if (b43_using_pio_transfers(dev))
+ b43_pio_tx_suspend(dev);
+ else
+ b43_dma_tx_suspend(dev);
}
/* Resume any TX operation on the device (resume the hardware queues) */
void b43_tx_resume(struct b43_wldev *dev)
{
- b43_dma_tx_resume(dev);
-}
-
-#if 0
-static void upload_qos_parms(struct b43_wldev *dev,
- const u16 * parms, u16 offset)
-{
- int i;
-
- for (i = 0; i < B43_NR_QOSPARMS; i++) {
- b43_shm_write16(dev, B43_SHM_SHARED,
- offset + (i * 2), parms[i]);
- }
-}
-#endif
-
-/* Initialize the QoS parameters */
-void b43_qos_init(struct b43_wldev *dev)
-{
- /* FIXME: This function must probably be called from the mac80211
- * config callback. */
- return;
-
- b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF);
- //FIXME kill magic
- b43_write16(dev, 0x688, b43_read16(dev, 0x688) | 0x4);
-
- /*TODO: We might need some stack support here to get the values. */
+ if (b43_using_pio_transfers(dev))
+ b43_pio_tx_resume(dev);
+ else
+ b43_dma_tx_resume(dev);
}
u8 * txhdr,
const unsigned char *fragment_data,
unsigned int fragment_len,
- const struct ieee80211_tx_control *txctl, u16 cookie);
+ const struct ieee80211_tx_info *txctl, u16 cookie);
/* Transmit Status */
struct b43_txstatus {
B43_TXST_SUPP_ABNACK, /* Afterburner NACK */
};
-/* Transmit Status as received through DMA/PIO on old chips */
-struct b43_hwtxstatus {
- PAD_BYTES(4);
- __le16 cookie;
- u8 flags;
- u8 count;
- PAD_BYTES(2);
- __le16 seq;
- u8 phy_stat;
- PAD_BYTES(1);
-} __attribute__ ((__packed__));
-
/* Receive header for v4 firmware. */
struct b43_rxhdr_fw4 {
__le16 frame_len; /* Frame length */
PAD_BYTES(2);
__le16 phy_status0; /* PHY RX Status 0 */
- __u8 jssi; /* PHY RX Status 1: JSSI */
- __u8 sig_qual; /* PHY RX Status 1: Signal Quality */
+ union {
+ /* RSSI for A/B/G-PHYs */
+ struct {
+ __u8 jssi; /* PHY RX Status 1: JSSI */
+ __u8 sig_qual; /* PHY RX Status 1: Signal Quality */
+ } __attribute__ ((__packed__));
+
+ /* RSSI for N-PHYs */
+ struct {
+ __s8 power0; /* PHY RX Status 1: Power 0 */
+ __s8 power1; /* PHY RX Status 1: Power 1 */
+ } __attribute__ ((__packed__));
+ } __attribute__ ((__packed__));
__le16 phy_status2; /* PHY RX Status 2 */
__le16 phy_status3; /* PHY RX Status 3 */
__le32 mac_status; /* MAC RX status */
void b43_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status);
-
-void b43_handle_hwtxstatus(struct b43_wldev *dev,
- const struct b43_hwtxstatus *hw);
+bool b43_fill_txstatus_report(struct ieee80211_tx_info *report,
+ const struct b43_txstatus *status);
void b43_tx_suspend(struct b43_wldev *dev);
void b43_tx_resume(struct b43_wldev *dev);
-#define B43_NR_QOSPARMS 22
-enum {
- B43_QOSPARM_TXOP = 0,
- B43_QOSPARM_CWMIN,
- B43_QOSPARM_CWMAX,
- B43_QOSPARM_CWCUR,
- B43_QOSPARM_AIFS,
- B43_QOSPARM_BSLOTS,
- B43_QOSPARM_REGGAP,
- B43_QOSPARM_STATUS,
-};
-void b43_qos_init(struct b43_wldev *dev);
/* Helper functions for converting the key-table index from "firmware-format"
* to "raw-format" and back. The firmware API changed for this at some revision.