Merge branch 'master' of ssh://master.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6

This commit is contained in:
David S. Miller 2010-02-14 17:45:59 -08:00
commit f6f223039c
79 changed files with 1462 additions and 1092 deletions

View file

@ -234,7 +234,6 @@ usage should require reading the full document.
<title>Multiple queues and QoS support</title>
<para>TBD</para>
!Finclude/net/mac80211.h ieee80211_tx_queue_params
!Finclude/net/mac80211.h ieee80211_tx_queue_stats
</chapter>
<chapter id="AP">

View file

@ -302,18 +302,6 @@ static int adm8211_get_stats(struct ieee80211_hw *dev,
return 0;
}
static int adm8211_get_tx_stats(struct ieee80211_hw *dev,
struct ieee80211_tx_queue_stats *stats)
{
struct adm8211_priv *priv = dev->priv;
stats[0].len = priv->cur_tx - priv->dirty_tx;
stats[0].limit = priv->tx_ring_size - 2;
stats[0].count = priv->dirty_tx;
return 0;
}
static void adm8211_interrupt_tci(struct ieee80211_hw *dev)
{
struct adm8211_priv *priv = dev->priv;
@ -1773,7 +1761,6 @@ static const struct ieee80211_ops adm8211_ops = {
.prepare_multicast = adm8211_prepare_multicast,
.configure_filter = adm8211_configure_filter,
.get_stats = adm8211_get_stats,
.get_tx_stats = adm8211_get_tx_stats,
.get_tsf = adm8211_get_tsft
};

View file

@ -144,6 +144,12 @@ struct ar9170_sta_tid {
bool active;
};
struct ar9170_tx_queue_stats {
unsigned int len;
unsigned int limit;
unsigned int count;
};
#define AR9170_QUEUE_TIMEOUT 64
#define AR9170_TX_TIMEOUT 8
#define AR9170_BA_TIMEOUT 4
@ -211,7 +217,7 @@ struct ar9170 {
/* qos queue settings */
spinlock_t tx_stats_lock;
struct ieee80211_tx_queue_stats tx_stats[5];
struct ar9170_tx_queue_stats tx_stats[5];
struct ieee80211_tx_queue_params edcf[5];
spinlock_t cmdlock;

View file

@ -2396,18 +2396,6 @@ static int ar9170_get_stats(struct ieee80211_hw *hw,
return 0;
}
static int ar9170_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *tx_stats)
{
struct ar9170 *ar = hw->priv;
spin_lock_bh(&ar->tx_stats_lock);
memcpy(tx_stats, ar->tx_stats, sizeof(tx_stats[0]) * hw->queues);
spin_unlock_bh(&ar->tx_stats_lock);
return 0;
}
static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue,
const struct ieee80211_tx_queue_params *param)
{
@ -2509,7 +2497,6 @@ static const struct ieee80211_ops ar9170_ops = {
.set_key = ar9170_set_key,
.sta_notify = ar9170_sta_notify,
.get_stats = ar9170_get_stats,
.get_tx_stats = ar9170_get_tx_stats,
.ampdu_action = ar9170_ampdu_action,
};

View file

@ -541,7 +541,6 @@ struct ath5k_txq_info {
/*
* Transmit packet types.
* used on tx control descriptor
* TODO: Use them inside base.c corectly
*/
enum ath5k_pkt_type {
AR5K_PKT_TYPE_NORMAL = 0,

View file

@ -241,8 +241,6 @@ static int ath5k_set_key(struct ieee80211_hw *hw,
struct ieee80211_key_conf *key);
static int ath5k_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats);
static int ath5k_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats);
static u64 ath5k_get_tsf(struct ieee80211_hw *hw);
static void ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf);
static void ath5k_reset_tsf(struct ieee80211_hw *hw);
@ -269,7 +267,6 @@ static const struct ieee80211_ops ath5k_hw_ops = {
.set_key = ath5k_set_key,
.get_stats = ath5k_get_stats,
.conf_tx = NULL,
.get_tx_stats = ath5k_get_tx_stats,
.get_tsf = ath5k_get_tsf,
.set_tsf = ath5k_set_tsf,
.reset_tsf = ath5k_reset_tsf,
@ -1249,6 +1246,29 @@ ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
return 0;
}
static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
{
struct ieee80211_hdr *hdr;
enum ath5k_pkt_type htype;
__le16 fc;
hdr = (struct ieee80211_hdr *)skb->data;
fc = hdr->frame_control;
if (ieee80211_is_beacon(fc))
htype = AR5K_PKT_TYPE_BEACON;
else if (ieee80211_is_probe_resp(fc))
htype = AR5K_PKT_TYPE_PROBE_RESP;
else if (ieee80211_is_atim(fc))
htype = AR5K_PKT_TYPE_ATIM;
else if (ieee80211_is_pspoll(fc))
htype = AR5K_PKT_TYPE_PSPOLL;
else
htype = AR5K_PKT_TYPE_NORMAL;
return htype;
}
static int
ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
struct ath5k_txq *txq)
@ -1303,7 +1323,8 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
sc->vif, pktlen, info));
}
ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL,
ieee80211_get_hdrlen_from_skb(skb),
get_hw_packet_type(skb),
(sc->power_level * 2),
hw_rate,
info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
@ -1332,7 +1353,6 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
spin_lock_bh(&txq->lock);
list_add_tail(&bf->list, &txq->q);
sc->tx_stats[txq->qnum].len++;
if (txq->link == NULL) /* is this first packet? */
ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
else /* no, so only link it */
@ -1581,7 +1601,6 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
ath5k_txbuf_free(sc, bf);
spin_lock_bh(&sc->txbuflock);
sc->tx_stats[txq->qnum].len--;
list_move_tail(&bf->list, &sc->txbuf);
sc->txbuf_len++;
spin_unlock_bh(&sc->txbuflock);
@ -2011,10 +2030,8 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
}
ieee80211_tx_status(sc->hw, skb);
sc->tx_stats[txq->qnum].count++;
spin_lock(&sc->txbuflock);
sc->tx_stats[txq->qnum].len--;
list_move_tail(&bf->list, &sc->txbuf);
sc->txbuf_len++;
spin_unlock(&sc->txbuflock);
@ -3116,17 +3133,6 @@ ath5k_get_stats(struct ieee80211_hw *hw,
return 0;
}
static int
ath5k_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats)
{
struct ath5k_softc *sc = hw->priv;
memcpy(stats, &sc->tx_stats, sizeof(sc->tx_stats));
return 0;
}
static u64
ath5k_get_tsf(struct ieee80211_hw *hw)
{

View file

@ -117,7 +117,6 @@ struct ath5k_softc {
struct pci_dev *pdev; /* for dma mapping */
void __iomem *iobase; /* address of the device */
struct mutex lock; /* dev-level lock */
struct ieee80211_tx_queue_stats tx_stats[AR5K_NUM_TX_QUEUES];
struct ieee80211_low_level_stats ll_stats;
struct ieee80211_hw *hw; /* IEEE 802.11 common */
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];

View file

@ -267,6 +267,7 @@ void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
u16 tid, u16 *ssn);
void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
void ath9k_enable_ps(struct ath_softc *sc);
/********/
/* VIFs */

View file

@ -577,6 +577,13 @@ static void ath_beacon_config_sta(struct ath_softc *sc,
u64 tsf;
int num_beacons, offset, dtim_dec_count, cfp_dec_count;
/* No need to configure beacon if we are not associated */
if (!common->curaid) {
ath_print(common, ATH_DBG_BEACON,
"STA is not yet associated..skipping beacon config\n");
return;
}
memset(&bs, 0, sizeof(bs));
intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
@ -739,7 +746,6 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
enum nl80211_iftype iftype;
/* Setup the beacon configuration parameters */
if (vif) {
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;

View file

@ -1217,6 +1217,17 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
/* As defined by IEEE 802.11-2007 17.3.8.6 */
slottime = ah->slottime + 3 * ah->coverage_class;
acktimeout = slottime + sifstime;
/*
* Workaround for early ACK timeouts, add an offset to match the
* initval's 64us ack timeout value.
* This was initially only meant to work around an issue with delayed
* BA frames in some implementations, but it has been found to fix ACK
* timeout issues in other cases as well.
*/
if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ)
acktimeout += 64 - sifstime - ah->slottime;
ath9k_hw_setslottime(ah, slottime);
ath9k_hw_set_ack_timeout(ah, acktimeout);
ath9k_hw_set_cts_timeout(ah, acktimeout);

View file

@ -622,7 +622,8 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_SPECTRUM_MGMT;
IEEE80211_HW_SPECTRUM_MGMT |
IEEE80211_HW_REPORTS_TX_ACK_STATUS;
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)
hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;

View file

@ -809,6 +809,7 @@ static void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf
clear_bit(key->hw_key_idx + 64, common->keymap);
if (common->splitmic) {
ath9k_hw_keyreset(ah, key->hw_key_idx + 32);
clear_bit(key->hw_key_idx + 32, common->keymap);
clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
}
@ -1492,6 +1493,19 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
mutex_unlock(&sc->mutex);
}
void ath9k_enable_ps(struct ath_softc *sc)
{
sc->ps_enabled = true;
if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) {
sc->imask |= ATH9K_INT_TIM_TIMER;
ath9k_hw_set_interrupts(sc->sc_ah,
sc->imask);
}
}
ath9k_hw_setrxabort(sc->sc_ah, 1);
}
static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
{
struct ath_wiphy *aphy = hw->priv;
@ -1546,22 +1560,13 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
if (changed & IEEE80211_CONF_CHANGE_PS) {
if (conf->flags & IEEE80211_CONF_PS) {
sc->ps_flags |= PS_ENABLED;
if (!(ah->caps.hw_caps &
ATH9K_HW_CAP_AUTOSLEEP)) {
if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) {
sc->imask |= ATH9K_INT_TIM_TIMER;
ath9k_hw_set_interrupts(sc->sc_ah,
sc->imask);
}
}
/*
* At this point we know hardware has received an ACK
* of a previously sent null data frame.
*/
if ((sc->ps_flags & PS_NULLFUNC_COMPLETED)) {
sc->ps_flags &= ~PS_NULLFUNC_COMPLETED;
sc->ps_enabled = true;
ath9k_hw_setrxabort(sc->sc_ah, 1);
ath9k_enable_ps(sc);
}
} else {
sc->ps_enabled = false;

View file

@ -2048,10 +2048,9 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
*/
if (bf->bf_isnullfunc &&
(ds->ds_txstat.ts_status & ATH9K_TX_ACKED)) {
if ((sc->ps_flags & PS_ENABLED)) {
sc->ps_enabled = true;
ath9k_hw_setrxabort(sc->sc_ah, 1);
} else
if ((sc->ps_flags & PS_ENABLED))
ath9k_enable_ps(sc);
else
sc->ps_flags |= PS_NULLFUNC_COMPLETED;
}

View file

@ -110,8 +110,9 @@ static const struct ieee80211_regdomain ath_world_regdom_67_68_6A = {
static inline bool is_wwr_sku(u16 regd)
{
return ((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
(regd == WORLD);
return ((regd & COUNTRY_ERD_FLAG) != COUNTRY_ERD_FLAG) &&
(((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
(regd == WORLD));
}
static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)

View file

@ -115,6 +115,7 @@
#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_IFSSLOT 0x684 /* Interframe slot time */
#define B43_MMIO_IFSCTL 0x688 /* Interframe space control */
#define B43_MMIO_IFSCTL_USE_EDCF 0x0004
#define B43_MMIO_POWERUP_DELAY 0x6A8

View file

@ -1369,7 +1369,6 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
b43err(dev->wl, "DMA tx mapping failure\n");
goto out;
}
ring->nr_tx_packets++;
if ((free_slots(ring) < TX_SLOTS_PER_FRAME) ||
should_inject_overflow(ring)) {
/* This TX ring is full. */
@ -1500,22 +1499,6 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
}
}
void b43_dma_get_tx_stats(struct b43_wldev *dev,
struct ieee80211_tx_queue_stats *stats)
{
const int nr_queues = dev->wl->hw->queues;
struct b43_dmaring *ring;
int i;
for (i = 0; i < nr_queues; i++) {
ring = select_ring_by_priority(dev, i);
stats[i].len = ring->used_slots / TX_SLOTS_PER_FRAME;
stats[i].limit = ring->nr_slots / TX_SLOTS_PER_FRAME;
stats[i].count = ring->nr_tx_packets;
}
}
static void dma_rx(struct b43_dmaring *ring, int *slot)
{
const struct b43_dma_ops *ops = ring->ops;

View file

@ -228,8 +228,6 @@ struct b43_dmaring {
int used_slots;
/* Currently used slot in the ring. */
int current_slot;
/* Total number of packets sent. Statistics only. */
unsigned int nr_tx_packets;
/* Frameoffset in octets. */
u32 frameoffset;
/* Descriptor buffer size. */
@ -278,9 +276,6 @@ void b43_dma_free(struct b43_wldev *dev);
void b43_dma_tx_suspend(struct b43_wldev *dev);
void b43_dma_tx_resume(struct b43_wldev *dev);
void b43_dma_get_tx_stats(struct b43_wldev *dev,
struct ieee80211_tx_queue_stats *stats);
int b43_dma_tx(struct b43_wldev *dev,
struct sk_buff *skb);
void b43_dma_handle_txstatus(struct b43_wldev *dev,

View file

@ -637,10 +637,17 @@ static void b43_upload_card_macaddress(struct b43_wldev *dev)
static void b43_set_slot_time(struct b43_wldev *dev, u16 slot_time)
{
/* slot_time is in usec. */
if (dev->phy.type != B43_PHYTYPE_G)
/* This test used to exit for all but a G PHY. */
if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
return;
b43_write16(dev, 0x684, 510 + slot_time);
b43_shm_write16(dev, B43_SHM_SHARED, 0x0010, slot_time);
b43_write16(dev, B43_MMIO_IFSSLOT, 510 + slot_time);
/* Shared memory location 0x0010 is the slot time and should be
* set to slot_time; however, this register is initially 0 and changing
* the value adversely affects the transmit rate for BCM4311
* devices. Until this behavior is unterstood, delete this step
*
* b43_shm_write16(dev, B43_SHM_SHARED, 0x0010, slot_time);
*/
}
static void b43_short_slot_timing_enable(struct b43_wldev *dev)
@ -3349,27 +3356,6 @@ out_unlock:
return err;
}
static int b43_op_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev;
int err = -ENODEV;
mutex_lock(&wl->mutex);
dev = wl->current_dev;
if (dev && b43_status(dev) >= B43_STAT_STARTED) {
if (b43_using_pio_transfers(dev))
b43_pio_get_tx_stats(dev, stats);
else
b43_dma_get_tx_stats(dev, stats);
err = 0;
}
mutex_unlock(&wl->mutex);
return err;
}
static int b43_op_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats)
{
@ -3980,6 +3966,7 @@ static int b43_wireless_core_start(struct b43_wldev *dev)
}
/* We are ready to run. */
ieee80211_wake_queues(dev->wl->hw);
b43_set_status(dev, B43_STAT_STARTED);
/* Start data flow (TX/RX). */
@ -4389,8 +4376,6 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
ieee80211_wake_queues(dev->wl->hw);
ieee80211_wake_queues(dev->wl->hw);
b43_set_status(dev, B43_STAT_INITIALIZED);
out:
@ -4596,7 +4581,6 @@ static const struct ieee80211_ops b43_hw_ops = {
.set_key = b43_op_set_key,
.update_tkip_key = b43_op_update_tkip_key,
.get_stats = b43_op_get_stats,
.get_tx_stats = b43_op_get_tx_stats,
.get_tsf = b43_op_get_tsf,
.set_tsf = b43_op_set_tsf,
.start = b43_op_start,

View file

@ -68,6 +68,10 @@ static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd,
u8 *events, u8 *delays, u8 length);
static void b43_nphy_force_rf_sequence(struct b43_wldev *dev,
enum b43_nphy_rf_sequence seq);
static void b43_nphy_rf_control_override(struct b43_wldev *dev, u16 field,
u16 value, u8 core, bool off);
static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field,
u16 value, u8 core);
void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
{//TODO
@ -498,8 +502,8 @@ static void b43_nphy_rx_cal_phy_setup(struct b43_wldev *dev, u8 core)
b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0007);
}
/* TODO: Call N PHY RF Ctrl Intc Override with 2, 0, 3 as arguments */
/* TODO: Call N PHY RF Intc Override with 8, 0, 3, 0 as arguments */
b43_nphy_rf_control_intc_override(dev, 2, 0, 3);
b43_nphy_rf_control_override(dev, 8, 0, 3, false);
b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX);
if (core == 0) {
@ -509,9 +513,8 @@ static void b43_nphy_rx_cal_phy_setup(struct b43_wldev *dev, u8 core)
rxval = 4;
txval = 2;
}
/* TODO: Call N PHY RF Ctrl Intc Override with 1, rxval, (core + 1) */
/* TODO: Call N PHY RF Ctrl Intc Override with 1, txval, (2 - core) */
b43_nphy_rf_control_intc_override(dev, 1, rxval, (core + 1));
b43_nphy_rf_control_intc_override(dev, 1, txval, (2 - core));
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalcRxIqComp */
@ -714,6 +717,67 @@ static void b43_nphy_stop_playback(struct b43_wldev *dev)
b43_nphy_stay_in_carrier_search(dev, 0);
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SpurWar */
static void b43_nphy_spur_workaround(struct b43_wldev *dev)
{
struct b43_phy_n *nphy = dev->phy.n;
unsigned int channel;
int tone[2] = { 57, 58 };
u32 noise[2] = { 0x3FF, 0x3FF };
B43_WARN_ON(dev->phy.rev < 3);
if (nphy->hang_avoid)
b43_nphy_stay_in_carrier_search(dev, 1);
/* FIXME: channel = radio_chanspec */
if (nphy->gband_spurwar_en) {
/* TODO: N PHY Adjust Analog Pfbw (7) */
if (channel == 11 && dev->phy.is_40mhz)
; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/
else
; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/
/* TODO: N PHY Adjust CRS Min Power (0x1E) */
}
if (nphy->aband_spurwar_en) {
if (channel == 54) {
tone[0] = 0x20;
noise[0] = 0x25F;
} else if (channel == 38 || channel == 102 || channel == 118) {
if (0 /* FIXME */) {
tone[0] = 0x20;
noise[0] = 0x21F;
} else {
tone[0] = 0;
noise[0] = 0;
}
} else if (channel == 134) {
tone[0] = 0x20;
noise[0] = 0x21F;
} else if (channel == 151) {
tone[0] = 0x10;
noise[0] = 0x23F;
} else if (channel == 153 || channel == 161) {
tone[0] = 0x30;
noise[0] = 0x23F;
} else {
tone[0] = 0;
noise[0] = 0;
}
if (!tone[0] && !noise[0])
; /* TODO: N PHY Adjust Min Noise Var(1, tone, noise)*/
else
; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/
}
if (nphy->hang_avoid)
b43_nphy_stay_in_carrier_search(dev, 0);
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/WorkaroundsGainCtrl */
static void b43_nphy_gain_crtl_workarounds(struct b43_wldev *dev)
{
@ -953,6 +1017,33 @@ static void b43_nphy_workarounds(struct b43_wldev *dev)
b43_nphy_stay_in_carrier_search(dev, 0);
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/LoadSampleTable */
static int b43_nphy_load_samples(struct b43_wldev *dev,
struct b43_c32 *samples, u16 len) {
struct b43_phy_n *nphy = dev->phy.n;
u16 i;
u32 *data;
data = kzalloc(len * sizeof(u32), GFP_KERNEL);
if (!data) {
b43err(dev->wl, "allocation for samples loading failed\n");
return -ENOMEM;
}
if (nphy->hang_avoid)
b43_nphy_stay_in_carrier_search(dev, 1);
for (i = 0; i < len; i++) {
data[i] = (samples[i].i & 0x3FF << 10);
data[i] |= samples[i].q & 0x3FF;
}
b43_ntab_write_bulk(dev, B43_NTAB32(17, 0), len, data);
kfree(data);
if (nphy->hang_avoid)
b43_nphy_stay_in_carrier_search(dev, 0);
return 0;
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GenLoadSamples */
static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
bool test)
@ -978,6 +1069,10 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
}
samples = kzalloc(len * sizeof(struct b43_c32), GFP_KERNEL);
if (!samples) {
b43err(dev->wl, "allocation for samples generation failed\n");
return 0;
}
rot = (((freq * 36) / bw) << 16) / 100;
angle = 0;
@ -988,9 +1083,9 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
samples[i].i = CORDIC_CONVERT(samples[i].i * max);
}
/* TODO: Call N PHY Load Sample Table with buffer, len as arguments */
i = b43_nphy_load_samples(dev, samples, len);
kfree(samples);
return len;
return (i < 0) ? 0 : len;
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */
@ -1264,6 +1359,104 @@ static void b43_nphy_rf_control_override(struct b43_wldev *dev, u16 field,
}
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlIntcOverride */
static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field,
u16 value, u8 core)
{
u8 i, j;
u16 reg, tmp, val;
B43_WARN_ON(dev->phy.rev < 3);
B43_WARN_ON(field > 4);
for (i = 0; i < 2; i++) {
if ((core == 1 && i == 1) || (core == 2 && !i))
continue;
reg = (i == 0) ?
B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2;
b43_phy_mask(dev, reg, 0xFBFF);
switch (field) {
case 0:
b43_phy_write(dev, reg, 0);
b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
break;
case 1:
if (!i) {
b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC1,
0xFC3F, (value << 6));
b43_phy_maskset(dev, B43_NPHY_TXF_40CO_B1S1,
0xFFFE, 1);
b43_phy_set(dev, B43_NPHY_RFCTL_CMD,
B43_NPHY_RFCTL_CMD_START);
for (j = 0; j < 100; j++) {
if (b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_START) {
j = 0;
break;
}
udelay(10);
}
if (j)
b43err(dev->wl,
"intc override timeout\n");
b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1,
0xFFFE);
} else {
b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC2,
0xFC3F, (value << 6));
b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER,
0xFFFE, 1);
b43_phy_set(dev, B43_NPHY_RFCTL_CMD,
B43_NPHY_RFCTL_CMD_RXTX);
for (j = 0; j < 100; j++) {
if (b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_RXTX) {
j = 0;
break;
}
udelay(10);
}
if (j)
b43err(dev->wl,
"intc override timeout\n");
b43_phy_mask(dev, B43_NPHY_RFCTL_OVER,
0xFFFE);
}
break;
case 2:
if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
tmp = 0x0020;
val = value << 5;
} else {
tmp = 0x0010;
val = value << 4;
}
b43_phy_maskset(dev, reg, ~tmp, val);
break;
case 3:
if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
tmp = 0x0001;
val = value;
} else {
tmp = 0x0004;
val = value << 2;
}
b43_phy_maskset(dev, reg, ~tmp, val);
break;
case 4:
if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
tmp = 0x0002;
val = value << 1;
} else {
tmp = 0x0008;
val = value << 3;
}
b43_phy_maskset(dev, reg, ~tmp, val);
break;
}
}
}
static void b43_nphy_bphy_init(struct b43_wldev *dev)
{
unsigned int i;
@ -2161,9 +2354,9 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
regs[7] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1);
regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2);
/* TODO: Call N PHY RF Ctrl Intc Override with 2, 1, 3 */
/* TODO: Call N PHY RF Ctrl Intc Override with 1, 2, 1 */
/* TODO: Call N PHY RF Ctrl Intc Override with 1, 8, 2 */
b43_nphy_rf_control_intc_override(dev, 2, 1, 3);
b43_nphy_rf_control_intc_override(dev, 1, 2, 1);
b43_nphy_rf_control_intc_override(dev, 1, 8, 2);
regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0);
regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1);
@ -2194,6 +2387,55 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
}
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SaveCal */
static void b43_nphy_save_cal(struct b43_wldev *dev)
{
struct b43_phy_n *nphy = dev->phy.n;
struct b43_phy_n_iq_comp *rxcal_coeffs = NULL;
u16 *txcal_radio_regs = NULL;
u8 *iqcal_chanspec;
u16 *table = NULL;
if (nphy->hang_avoid)
b43_nphy_stay_in_carrier_search(dev, 1);
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G;
txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G;
iqcal_chanspec = &nphy->iqcal_chanspec_2G;
table = nphy->cal_cache.txcal_coeffs_2G;
} else {
rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G;
txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G;
iqcal_chanspec = &nphy->iqcal_chanspec_5G;
table = nphy->cal_cache.txcal_coeffs_5G;
}
b43_nphy_rx_iq_coeffs(dev, false, rxcal_coeffs);
/* TODO use some definitions */
if (dev->phy.rev >= 3) {
txcal_radio_regs[0] = b43_radio_read(dev, 0x2021);
txcal_radio_regs[1] = b43_radio_read(dev, 0x2022);
txcal_radio_regs[2] = b43_radio_read(dev, 0x3021);
txcal_radio_regs[3] = b43_radio_read(dev, 0x3022);
txcal_radio_regs[4] = b43_radio_read(dev, 0x2023);
txcal_radio_regs[5] = b43_radio_read(dev, 0x2024);
txcal_radio_regs[6] = b43_radio_read(dev, 0x3023);
txcal_radio_regs[7] = b43_radio_read(dev, 0x3024);
} else {
txcal_radio_regs[0] = b43_radio_read(dev, 0x8B);
txcal_radio_regs[1] = b43_radio_read(dev, 0xBA);
txcal_radio_regs[2] = b43_radio_read(dev, 0x8D);
txcal_radio_regs[3] = b43_radio_read(dev, 0xBC);
}
*iqcal_chanspec = nphy->radio_chanspec;
b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 8, table);
if (nphy->hang_avoid)
b43_nphy_stay_in_carrier_search(dev, 0);
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */
static void b43_nphy_restore_cal(struct b43_wldev *dev)
{
@ -2486,6 +2728,39 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
return error;
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ReapplyTxCalCoeffs */
static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev)
{
struct b43_phy_n *nphy = dev->phy.n;
u8 i;
u16 buffer[7];
bool equal = true;
if (!nphy->txiqlocal_coeffsvalid || 1 /* FIXME */)
return;
b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer);
for (i = 0; i < 4; i++) {
if (buffer[i] != nphy->txiqlocal_bestc[i]) {
equal = false;
break;
}
}
if (!equal) {
b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4,
nphy->txiqlocal_bestc);
for (i = 0; i < 4; i++)
buffer[i] = 0;
b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4,
buffer);
b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2,
&nphy->txiqlocal_bestc[5]);
b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2,
&nphy->txiqlocal_bestc[5]);
}
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIqRev2 */
static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
struct nphy_txgains target, u8 type, bool debug)
@ -2516,7 +2791,7 @@ static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
b43_nphy_stay_in_carrier_search(dev, 1);
if (dev->phy.rev < 2)
;/* TODO: Call N PHY Reapply TX Cal Coeffs */
b43_nphy_reapply_tx_cal_coeffs(dev);
b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save);
for (i = 0; i < 2; i++) {
b43_nphy_iq_cal_gain_params(dev, i, target, &cal_params[i]);
@ -2858,7 +3133,7 @@ int b43_phy_initn(struct b43_wldev *dev)
if (!b43_nphy_cal_tx_iq_lo(dev, target, true, false)) {
if (b43_nphy_cal_rx_iq(dev, target, 2, 0) == 0)
;/* Call N PHY Save Cal */
b43_nphy_save_cal(dev);
else if (nphy->mphase_cal_phase_id == 0)
;/* N PHY Periodic Calibration with argument 3 */
} else {
@ -2872,7 +3147,8 @@ int b43_phy_initn(struct b43_wldev *dev)
if (phy->rev >= 3 && phy->rev <= 6)
b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0014);
b43_nphy_tx_lp_fbw(dev);
/* TODO N PHY Spur Workaround */
if (phy->rev >= 3)
b43_nphy_spur_workaround(dev);
b43err(dev->wl, "IEEE 802.11n devices are not supported, yet.\n");
return 0;

View file

@ -975,6 +975,7 @@ struct b43_phy_n {
u16 papd_epsilon_offset[2];
s32 preamble_override;
u32 bb_mult_save;
u16 radio_chanspec;
bool gain_boost;
bool elna_gain_config;
@ -1001,6 +1002,9 @@ struct b43_phy_n {
u16 classifier_state;
u16 clip_state[2];
bool aband_spurwar_en;
bool gband_spurwar_en;
bool ipa2g_on;
u8 iqcal_chanspec_2G;
u8 rssical_chanspec_2G;

View file

@ -559,7 +559,6 @@ int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb)
b43err(dev->wl, "PIO transmission failure\n");
goto out;
}
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)) ||
@ -605,22 +604,6 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev,
}
}
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;
int i;
for (i = 0; i < nr_queues; i++) {
q = select_queue_by_priority(dev, i);
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;
}
}
/* Returns whether we should fetch another frame. */
static bool pio_rx_frame(struct b43_pio_rxqueue *q)
{

View file

@ -90,9 +90,6 @@ struct b43_pio_txqueue {
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;
@ -160,8 +157,6 @@ 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);

View file

@ -1411,7 +1411,6 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev,
b43legacyerr(dev->wl, "DMA tx mapping failure\n");
goto out_unlock;
}
ring->nr_tx_packets++;
if ((free_slots(ring) < SLOTS_PER_PACKET) ||
should_inject_overflow(ring)) {
/* This TX ring is full. */
@ -1527,25 +1526,6 @@ void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
spin_unlock(&ring->lock);
}
void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats)
{
const int nr_queues = dev->wl->hw->queues;
struct b43legacy_dmaring *ring;
unsigned long flags;
int i;
for (i = 0; i < nr_queues; i++) {
ring = priority_to_txring(dev, i);
spin_lock_irqsave(&ring->lock, flags);
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);
}
}
static void dma_rx(struct b43legacy_dmaring *ring,
int *slot)
{

View file

@ -243,8 +243,6 @@ struct b43legacy_dmaring {
int used_slots;
/* Currently used slot in the ring. */
int current_slot;
/* Total number of packets sent. Statistics only. */
unsigned int nr_tx_packets;
/* Frameoffset in octets. */
u32 frameoffset;
/* Descriptor buffer size. */
@ -292,9 +290,6 @@ void b43legacy_dma_free(struct b43legacy_wldev *dev);
void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev);
void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev);
void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats);
int b43legacy_dma_tx(struct b43legacy_wldev *dev,
struct sk_buff *skb);
void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
@ -315,11 +310,6 @@ void b43legacy_dma_free(struct b43legacy_wldev *dev)
{
}
static inline
void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats)
{
}
static inline
int b43legacy_dma_tx(struct b43legacy_wldev *dev,
struct sk_buff *skb)
{

View file

@ -2446,29 +2446,6 @@ static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
return 0;
}
static int b43legacy_op_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats)
{
struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
struct b43legacy_wldev *dev = wl->current_dev;
unsigned long flags;
int err = -ENODEV;
if (!dev)
goto out;
spin_lock_irqsave(&wl->irq_lock, flags);
if (likely(b43legacy_status(dev) >= B43legacy_STAT_STARTED)) {
if (b43legacy_using_pio(dev))
b43legacy_pio_get_tx_stats(dev, stats);
else
b43legacy_dma_get_tx_stats(dev, stats);
err = 0;
}
spin_unlock_irqrestore(&wl->irq_lock, flags);
out:
return err;
}
static int b43legacy_op_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats)
{
@ -2923,6 +2900,7 @@ static int b43legacy_wireless_core_start(struct b43legacy_wldev *dev)
goto out;
}
/* We are ready to run. */
ieee80211_wake_queues(dev->wl->hw);
b43legacy_set_status(dev, B43legacy_STAT_STARTED);
/* Start data flow (TX/RX) */
@ -3343,6 +3321,7 @@ static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev)
b43legacy_security_init(dev);
b43legacy_rng_init(wl);
ieee80211_wake_queues(dev->wl->hw);
b43legacy_set_status(dev, B43legacy_STAT_INITIALIZED);
b43legacy_leds_init(dev);
@ -3511,7 +3490,6 @@ static const struct ieee80211_ops b43legacy_hw_ops = {
.bss_info_changed = b43legacy_op_bss_info_changed,
.configure_filter = b43legacy_op_configure_filter,
.get_stats = b43legacy_op_get_stats,
.get_tx_stats = b43legacy_op_get_tx_stats,
.start = b43legacy_op_start,
.stop = b43legacy_op_stop,
.set_tim = b43legacy_op_beacon_set_tim,

View file

@ -477,7 +477,6 @@ int b43legacy_pio_tx(struct b43legacy_wldev *dev,
list_move_tail(&packet->list, &queue->txqueue);
queue->nr_txfree--;
queue->nr_tx_packets++;
B43legacy_WARN_ON(queue->nr_txfree >= B43legacy_PIO_MAXTXPACKETS);
tasklet_schedule(&queue->txtask);
@ -546,18 +545,6 @@ void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev,
tasklet_schedule(&queue->txtask);
}
void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats)
{
struct b43legacy_pio *pio = &dev->pio;
struct b43legacy_pioqueue *queue;
queue = pio->queue1;
stats[0].len = B43legacy_PIO_MAXTXPACKETS - queue->nr_txfree;
stats[0].limit = B43legacy_PIO_MAXTXPACKETS;
stats[0].count = queue->nr_tx_packets;
}
static void pio_rx_error(struct b43legacy_pioqueue *queue,
int clear_buffers,
const char *error)

View file

@ -74,10 +74,6 @@ struct b43legacy_pioqueue {
* posted to the device. We are waiting for the txstatus.
*/
struct list_head txrunning;
/* Total number or packets sent.
* (This counter can obviously wrap).
*/
unsigned int nr_tx_packets;
struct tasklet_struct txtask;
struct b43legacy_pio_txpacket
tx_packets_cache[B43legacy_PIO_MAXTXPACKETS];
@ -106,8 +102,6 @@ int b43legacy_pio_tx(struct b43legacy_wldev *dev,
struct sk_buff *skb);
void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status);
void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats);
void b43legacy_pio_rx(struct b43legacy_pioqueue *queue);
/* Suspend TX queue in hardware. */
@ -140,11 +134,6 @@ void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev,
{
}
static inline
void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats)
{
}
static inline
void b43legacy_pio_rx(struct b43legacy_pioqueue *queue)
{
}

View file

@ -247,6 +247,7 @@ struct iwl_cfg iwl1000_bgn_cfg = {
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
struct iwl_cfg iwl1000_bg_cfg = {
@ -274,6 +275,7 @@ struct iwl_cfg iwl1000_bg_cfg = {
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX));

View file

@ -179,14 +179,24 @@ static void iwl5000_gain_computation(struct iwl_priv *priv,
data->delta_gain_code[i] = 0;
continue;
}
delta_g = (1000 * ((s32)average_noise[default_chain] -
delta_g = (priv->cfg->chain_noise_scale *
((s32)average_noise[default_chain] -
(s32)average_noise[i])) / 1500;
/* bound gain by 2 bits value max, 3rd bit is sign */
data->delta_gain_code[i] =
min(abs(delta_g), (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
if (delta_g < 0)
/* set negative sign */
/*
* set negative sign ...
* note to Intel developers: This is uCode API format,
* not the format of any internal device registers.
* Do not change this format for e.g. 6050 or similar
* devices. Change format only if more resolution
* (i.e. more than 2 bits magnitude) is needed.
*/
data->delta_gain_code[i] |= (1 << 2);
}
@ -1587,6 +1597,7 @@ struct iwl_cfg iwl5300_agn_cfg = {
.use_rts_for_ht = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
struct iwl_cfg iwl5100_bgn_cfg = {
@ -1612,6 +1623,7 @@ struct iwl_cfg iwl5100_bgn_cfg = {
.use_rts_for_ht = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
struct iwl_cfg iwl5100_abg_cfg = {
@ -1635,6 +1647,7 @@ struct iwl_cfg iwl5100_abg_cfg = {
.led_compensation = 51,
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
struct iwl_cfg iwl5100_agn_cfg = {
@ -1660,6 +1673,7 @@ struct iwl_cfg iwl5100_agn_cfg = {
.use_rts_for_ht = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
struct iwl_cfg iwl5350_agn_cfg = {
@ -1685,6 +1699,7 @@ struct iwl_cfg iwl5350_agn_cfg = {
.use_rts_for_ht = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
struct iwl_cfg iwl5150_agn_cfg = {
@ -1710,6 +1725,7 @@ struct iwl_cfg iwl5150_agn_cfg = {
.use_rts_for_ht = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
struct iwl_cfg iwl5150_abg_cfg = {
@ -1733,6 +1749,7 @@ struct iwl_cfg iwl5150_abg_cfg = {
.led_compensation = 51,
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));

View file

@ -277,21 +277,6 @@ static const struct iwl_ops iwl6000_ops = {
.led = &iwlagn_led_ops,
};
static struct iwl_hcmd_utils_ops iwl6050_hcmd_utils = {
.get_hcmd_size = iwl5000_get_hcmd_size,
.build_addsta_hcmd = iwl5000_build_addsta_hcmd,
.rts_tx_cmd_flag = iwl5000_rts_tx_cmd_flag,
.calc_rssi = iwl5000_calc_rssi,
};
static const struct iwl_ops iwl6050_ops = {
.ucode = &iwl5000_ucode,
.lib = &iwl6000_lib,
.hcmd = &iwl5000_hcmd,
.utils = &iwl6050_hcmd_utils,
.led = &iwlagn_led_ops,
};
/*
* "i": Internal configuration, use internal Power Amplifier
*/
@ -324,6 +309,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
struct iwl_cfg iwl6000i_2abg_cfg = {
@ -354,6 +340,7 @@ struct iwl_cfg iwl6000i_2abg_cfg = {
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
struct iwl_cfg iwl6000i_2bg_cfg = {
@ -384,6 +371,7 @@ struct iwl_cfg iwl6000i_2bg_cfg = {
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
struct iwl_cfg iwl6050_2agn_cfg = {
@ -392,7 +380,7 @@ struct iwl_cfg iwl6050_2agn_cfg = {
.ucode_api_max = IWL6050_UCODE_API_MAX,
.ucode_api_min = IWL6050_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.ops = &iwl6050_ops,
.ops = &iwl6000_ops,
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.eeprom_ver = EEPROM_6050_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
@ -415,6 +403,7 @@ struct iwl_cfg iwl6050_2agn_cfg = {
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1500,
};
struct iwl_cfg iwl6050_2abg_cfg = {
@ -423,7 +412,7 @@ struct iwl_cfg iwl6050_2abg_cfg = {
.ucode_api_max = IWL6050_UCODE_API_MAX,
.ucode_api_min = IWL6050_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G,
.ops = &iwl6050_ops,
.ops = &iwl6000_ops,
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.eeprom_ver = EEPROM_6050_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
@ -445,6 +434,7 @@ struct iwl_cfg iwl6050_2abg_cfg = {
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1500,
};
struct iwl_cfg iwl6000_3agn_cfg = {
@ -476,6 +466,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000,
};
MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));

View file

@ -2631,7 +2631,7 @@ static int iwl_mac_setup_register(struct iwl_priv *priv)
*/
hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX + 1;
/* we create the 802.11 header and a zero-length SSID element */
hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2;
@ -3440,7 +3440,6 @@ static struct ieee80211_ops iwl_hw_ops = {
.set_key = iwl_mac_set_key,
.update_tkip_key = iwl_mac_update_tkip_key,
.get_stats = iwl_mac_get_stats,
.get_tx_stats = iwl_mac_get_tx_stats,
.conf_tx = iwl_mac_conf_tx,
.reset_tsf = iwl_mac_reset_tsf,
.bss_info_changed = iwl_bss_info_changed,

View file

@ -2787,6 +2787,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
if ((le16_to_cpu(priv->staging_rxon.channel) != ch))
priv->staging_rxon.flags = 0;
iwl_set_rxon_ht(priv, ht_conf);
iwl_set_rxon_channel(priv, conf->channel);
iwl_set_flags_for_band(priv, conf->channel->band);
@ -2850,42 +2851,6 @@ out:
}
EXPORT_SYMBOL(iwl_mac_config);
int iwl_mac_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats)
{
struct iwl_priv *priv = hw->priv;
int i, avail;
struct iwl_tx_queue *txq;
struct iwl_queue *q;
unsigned long flags;
IWL_DEBUG_MAC80211(priv, "enter\n");
if (!iwl_is_ready_rf(priv)) {
IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n");
return -EIO;
}
spin_lock_irqsave(&priv->lock, flags);
for (i = 0; i < AC_NUM; i++) {
txq = &priv->txq[i];
q = &txq->q;
avail = iwl_queue_space(q);
stats[i].len = q->n_window - avail;
stats[i].limit = q->n_window - q->high_mark;
stats[i].count = q->n_window;
}
spin_unlock_irqrestore(&priv->lock, flags);
IWL_DEBUG_MAC80211(priv, "leave\n");
return 0;
}
EXPORT_SYMBOL(iwl_mac_get_tx_stats);
void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
{
struct iwl_priv *priv = hw->priv;

View file

@ -293,6 +293,7 @@ struct iwl_cfg {
bool support_ct_kill_exit;
const bool support_wimax_coexist;
u8 plcp_delta_threshold;
s32 chain_noise_scale;
};
/***************************
@ -341,8 +342,6 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
int iwl_mac_config(struct ieee80211_hw *hw, u32 changed);
void iwl_config_ap(struct iwl_priv *priv);
int iwl_mac_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats);
void iwl_mac_reset_tsf(struct ieee80211_hw *hw);
int iwl_alloc_txq_mem(struct iwl_priv *priv);
void iwl_free_txq_mem(struct iwl_priv *priv);

View file

@ -651,9 +651,20 @@ u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
if (left < 0)
return 0;
*pos++ = WLAN_EID_SSID;
*pos++ = 0;
if (!priv->is_internal_short_scan &&
priv->scan_request->n_ssids) {
struct cfg80211_ssid *ssid =
priv->scan_request->ssids;
len += 2;
/* Broadcast if ssid_len is 0 */
*pos++ = ssid->ssid_len;
memcpy(pos, ssid->ssid, ssid->ssid_len);
pos += ssid->ssid_len;
len += 2 + ssid->ssid_len;
} else {
*pos++ = 0;
len += 2;
}
if (WARN_ON(left < ie_len))
return len;
@ -782,20 +793,26 @@ static void iwl_bg_request_scan(struct work_struct *data)
if (priv->is_internal_short_scan) {
IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
} else if (priv->scan_request->n_ssids) {
int i, p = 0;
IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
for (i = 0; i < priv->scan_request->n_ssids; i++) {
/* always does wildcard anyway */
if (!priv->scan_request->ssids[i].ssid_len)
continue;
scan->direct_scan[p].id = WLAN_EID_SSID;
scan->direct_scan[p].len =
priv->scan_request->ssids[i].ssid_len;
memcpy(scan->direct_scan[p].ssid,
priv->scan_request->ssids[i].ssid,
priv->scan_request->ssids[i].ssid_len);
n_probes++;
p++;
/*
* The first SSID to scan is stuffed into the probe request
* template and the remaining ones are handled through the
* direct_scan array.
*/
if (priv->scan_request->n_ssids > 1) {
int i, p = 0;
for (i = 1; i < priv->scan_request->n_ssids; i++) {
if (!priv->scan_request->ssids[i].ssid_len)
continue;
scan->direct_scan[p].id = WLAN_EID_SSID;
scan->direct_scan[p].len =
priv->scan_request->ssids[i].ssid_len;
memcpy(scan->direct_scan[p].ssid,
priv->scan_request->ssids[i].ssid,
priv->scan_request->ssids[i].ssid_len);
n_probes++;
p++;
}
}
is_active = true;
} else

View file

@ -3835,7 +3835,6 @@ static struct ieee80211_ops iwl3945_hw_ops = {
.config = iwl_mac_config,
.configure_filter = iwl_configure_filter,
.set_key = iwl3945_mac_set_key,
.get_tx_stats = iwl_mac_get_tx_stats,
.conf_tx = iwl_mac_conf_tx,
.reset_tsf = iwl_mac_reset_tsf,
.bss_info_changed = iwl_bss_info_changed,

View file

@ -240,11 +240,6 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
/* Now we got response from FW, cancel the command timer */
del_timer(&priv->command_timer);
priv->cmd_timed_out = 0;
if (priv->nr_retries) {
lbs_pr_info("Received result %x to command %x after %d retries\n",
result, curcmd, priv->nr_retries);
priv->nr_retries = 0;
}
/* Store the response code to cur_cmd_retcode. */
priv->cur_cmd_retcode = result;

View file

@ -109,7 +109,6 @@ struct lbs_private {
struct list_head cmdpendingq; /* pending command buffers */
wait_queue_head_t cmd_pending;
struct timer_list command_timer;
int nr_retries;
int cmd_timed_out;
/* Command responses sent from the hardware to the driver */

View file

@ -23,6 +23,7 @@
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/semaphore.h>
#include <linux/spi/libertas_spi.h>
#include <linux/spi/spi.h>

View file

@ -536,31 +536,14 @@ static int lbs_thread(void *data)
if (priv->cmd_timed_out && priv->cur_cmd) {
struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
if (++priv->nr_retries > 3) {
lbs_pr_info("Excessive timeouts submitting "
"command 0x%04x\n",
le16_to_cpu(cmdnode->cmdbuf->command));
lbs_complete_command(priv, cmdnode, -ETIMEDOUT);
priv->nr_retries = 0;
if (priv->reset_card)
priv->reset_card(priv);
} else {
priv->cur_cmd = NULL;
priv->dnld_sent = DNLD_RES_RECEIVED;
lbs_pr_info("requeueing command 0x%04x due "
"to timeout (#%d)\n",
le16_to_cpu(cmdnode->cmdbuf->command),
priv->nr_retries);
/* Stick it back at the _top_ of the pending queue
for immediate resubmission */
list_add(&cmdnode->list, &priv->cmdpendingq);
}
lbs_pr_info("Timeout submitting command 0x%04x\n",
le16_to_cpu(cmdnode->cmdbuf->command));
lbs_complete_command(priv, cmdnode, -ETIMEDOUT);
if (priv->reset_card)
priv->reset_card(priv);
}
priv->cmd_timed_out = 0;
if (!priv->fw_ready)
continue;
@ -732,7 +715,7 @@ done:
* This function handles the timeout of command sending.
* It will re-send the same command again.
*/
static void command_timer_fn(unsigned long data)
static void lbs_cmd_timeout_handler(unsigned long data)
{
struct lbs_private *priv = (struct lbs_private *)data;
unsigned long flags;
@ -851,7 +834,7 @@ static int lbs_init_adapter(struct lbs_private *priv)
mutex_init(&priv->lock);
setup_timer(&priv->command_timer, command_timer_fn,
setup_timer(&priv->command_timer, lbs_cmd_timeout_handler,
(unsigned long)priv);
setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn,
(unsigned long)priv);

View file

@ -555,6 +555,9 @@ struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev)
priv->band.n_channels = ARRAY_SIZE(lbtf_channels);
priv->band.channels = priv->channels;
hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC);
skb_queue_head_init(&priv->bc_ps_buf);
SET_IEEE80211_DEV(hw, dmdev);

View file

@ -32,6 +32,10 @@ static int radios = 2;
module_param(radios, int, 0444);
MODULE_PARM_DESC(radios, "Number of simulated radios");
static bool fake_hw_scan;
module_param(fake_hw_scan, bool, 0444);
MODULE_PARM_DESC(fake_hw_scan, "Install fake (no-op) hw-scan handler");
/**
* enum hwsim_regtest - the type of regulatory tests we offer
*
@ -908,8 +912,43 @@ static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
*/
}
struct hw_scan_done {
struct delayed_work w;
struct ieee80211_hw *hw;
};
static const struct ieee80211_ops mac80211_hwsim_ops =
static void hw_scan_done(struct work_struct *work)
{
struct hw_scan_done *hsd =
container_of(work, struct hw_scan_done, w.work);
ieee80211_scan_completed(hsd->hw, false);
kfree(hsd);
}
static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
struct cfg80211_scan_request *req)
{
struct hw_scan_done *hsd = kzalloc(sizeof(*hsd), GFP_KERNEL);
int i;
if (!hsd)
return -ENOMEM;
hsd->hw = hw;
INIT_DELAYED_WORK(&hsd->w, hw_scan_done);
printk(KERN_DEBUG "hwsim scan request\n");
for (i = 0; i < req->n_channels; i++)
printk(KERN_DEBUG "hwsim scan freq %d\n",
req->channels[i]->center_freq);
ieee80211_queue_delayed_work(hw, &hsd->w, 2 * HZ);
return 0;
}
static struct ieee80211_ops mac80211_hwsim_ops =
{
.tx = mac80211_hwsim_tx,
.start = mac80211_hwsim_start,
@ -1119,6 +1158,9 @@ static int __init init_mac80211_hwsim(void)
if (radios < 1 || radios > 100)
return -EINVAL;
if (fake_hw_scan)
mac80211_hwsim_ops.hw_scan = mac80211_hwsim_hw_scan;
spin_lock_init(&hwsim_radio_lock);
INIT_LIST_HEAD(&hwsim_radios);

View file

@ -119,7 +119,7 @@ struct mwl8k_tx_queue {
/* sw appends here */
int tail;
struct ieee80211_tx_queue_stats stats;
unsigned int len;
struct mwl8k_tx_desc *txd;
dma_addr_t txd_dma;
struct sk_buff **skb;
@ -1136,8 +1136,7 @@ static int mwl8k_txq_init(struct ieee80211_hw *hw, int index)
int size;
int i;
memset(&txq->stats, 0, sizeof(struct ieee80211_tx_queue_stats));
txq->stats.limit = MWL8K_TX_DESCS;
txq->len = 0;
txq->head = 0;
txq->tail = 0;
@ -1213,7 +1212,7 @@ static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw)
printk(KERN_ERR "%s: txq[%d] len=%d head=%d tail=%d "
"fw_owned=%d drv_owned=%d unused=%d\n",
wiphy_name(hw->wiphy), i,
txq->stats.len, txq->head, txq->tail,
txq->len, txq->head, txq->tail,
fw_owned, drv_owned, unused);
}
}
@ -1299,7 +1298,7 @@ mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)
int processed;
processed = 0;
while (txq->stats.len > 0 && limit--) {
while (txq->len > 0 && limit--) {
int tx;
struct mwl8k_tx_desc *tx_desc;
unsigned long addr;
@ -1321,8 +1320,8 @@ mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)
}
txq->head = (tx + 1) % MWL8K_TX_DESCS;
BUG_ON(txq->stats.len == 0);
txq->stats.len--;
BUG_ON(txq->len == 0);
txq->len--;
priv->pending_tx_pkts--;
addr = le32_to_cpu(tx_desc->pkt_phys_addr);
@ -1454,8 +1453,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
wmb();
tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus);
txq->stats.count++;
txq->stats.len++;
txq->len++;
priv->pending_tx_pkts++;
txq->tail++;
@ -3818,24 +3816,6 @@ static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,
return rc;
}
static int mwl8k_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats)
{
struct mwl8k_priv *priv = hw->priv;
struct mwl8k_tx_queue *txq;
int index;
spin_lock_bh(&priv->tx_lock);
for (index = 0; index < MWL8K_TX_QUEUES; index++) {
txq = priv->txq + index;
memcpy(&stats[index], &txq->stats,
sizeof(struct ieee80211_tx_queue_stats));
}
spin_unlock_bh(&priv->tx_lock);
return 0;
}
static int mwl8k_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats)
{
@ -3871,7 +3851,6 @@ static const struct ieee80211_ops mwl8k_ops = {
.set_rts_threshold = mwl8k_set_rts_threshold,
.sta_notify = mwl8k_sta_notify,
.conf_tx = mwl8k_conf_tx,
.get_tx_stats = mwl8k_get_tx_stats,
.get_stats = mwl8k_get_stats,
.ampdu_action = mwl8k_ampdu_action,
};

View file

@ -358,16 +358,6 @@ static int p54_get_stats(struct ieee80211_hw *dev,
return 0;
}
static int p54_get_tx_stats(struct ieee80211_hw *dev,
struct ieee80211_tx_queue_stats *stats)
{
struct p54_common *priv = dev->priv;
memcpy(stats, &priv->tx_stats[P54_QUEUE_DATA],
sizeof(stats[0]) * dev->queues);
return 0;
}
static void p54_bss_info_changed(struct ieee80211_hw *dev,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info,
@ -522,7 +512,6 @@ static const struct ieee80211_ops p54_ops = {
.configure_filter = p54_configure_filter,
.conf_tx = p54_conf_tx,
.get_stats = p54_get_stats,
.get_tx_stats = p54_get_tx_stats
};
struct ieee80211_hw *p54_init_common(size_t priv_data_len)

View file

@ -157,6 +157,12 @@ struct p54_led_dev {
#endif /* CONFIG_P54_LEDS */
struct p54_tx_queue_stats {
unsigned int len;
unsigned int limit;
unsigned int count;
};
struct p54_common {
struct ieee80211_hw *hw;
struct ieee80211_vif *vif;
@ -183,7 +189,7 @@ struct p54_common {
/* (e)DCF / QOS state */
bool use_short_slot;
spinlock_t tx_stats_lock;
struct ieee80211_tx_queue_stats tx_stats[8];
struct p54_tx_queue_stats tx_stats[8];
struct p54_edcf_queue_param qos_params[8];
/* Radio data */

View file

@ -183,7 +183,7 @@ static int p54_tx_qos_accounting_alloc(struct p54_common *priv,
struct sk_buff *skb,
const u16 p54_queue)
{
struct ieee80211_tx_queue_stats *queue;
struct p54_tx_queue_stats *queue;
unsigned long flags;
if (WARN_ON(p54_queue > P54_QUEUE_NUM))

View file

@ -1561,7 +1561,6 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = {
.get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2400pci_conf_tx,
.get_tx_stats = rt2x00mac_get_tx_stats,
.get_tsf = rt2400pci_get_tsf,
.tx_last_beacon = rt2400pci_tx_last_beacon,
.rfkill_poll = rt2x00mac_rfkill_poll,

View file

@ -1859,7 +1859,6 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = {
.get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2x00mac_conf_tx,
.get_tx_stats = rt2x00mac_get_tx_stats,
.get_tsf = rt2500pci_get_tsf,
.tx_last_beacon = rt2500pci_tx_last_beacon,
.rfkill_poll = rt2x00mac_rfkill_poll,

View file

@ -1761,7 +1761,6 @@ static const struct ieee80211_ops rt2500usb_mac80211_ops = {
.get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2x00mac_conf_tx,
.get_tx_stats = rt2x00mac_get_tx_stats,
.rfkill_poll = rt2x00mac_rfkill_poll,
};

View file

@ -2297,7 +2297,6 @@ const struct ieee80211_ops rt2800_mac80211_ops = {
.set_rts_threshold = rt2800_set_rts_threshold,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2800_conf_tx,
.get_tx_stats = rt2x00mac_get_tx_stats,
.get_tsf = rt2800_get_tsf,
.rfkill_poll = rt2x00mac_rfkill_poll,
};

View file

@ -1044,8 +1044,6 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
#endif /* CONFIG_RT2X00_LIB_CRYPTO */
int rt2x00mac_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats);
int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats);
void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,

View file

@ -555,22 +555,6 @@ int rt2x00mac_get_stats(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL_GPL(rt2x00mac_get_stats);
int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
unsigned int i;
for (i = 0; i < rt2x00dev->ops->tx_queues; i++) {
stats[i].len = rt2x00dev->tx[i].length;
stats[i].limit = rt2x00dev->tx[i].limit;
stats[i].count = rt2x00dev->tx[i].count;
}
return 0;
}
EXPORT_SYMBOL_GPL(rt2x00mac_get_tx_stats);
void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,

View file

@ -2730,7 +2730,6 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = {
.get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt61pci_conf_tx,
.get_tx_stats = rt2x00mac_get_tx_stats,
.get_tsf = rt61pci_get_tsf,
.rfkill_poll = rt2x00mac_rfkill_poll,
};

View file

@ -2245,7 +2245,6 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = {
.get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt73usb_conf_tx,
.get_tx_stats = rt2x00mac_get_tx_stats,
.get_tsf = rt73usb_get_tsf,
.rfkill_poll = rt2x00mac_rfkill_poll,
};

View file

@ -1144,9 +1144,10 @@ static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
if (ret < 0)
goto out;
/* mac80211 uses units of 32 usec */
ret = wl1251_acx_ac_cfg(wl, wl1251_tx_get_queue(queue),
params->cw_min, params->cw_max,
params->aifs, params->txop);
params->aifs, params->txop * 32);
if (ret < 0)
goto out_sleep;

View file

@ -270,7 +270,6 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore)
set_irq(dev, irq++);
}
break;
/* fallthrough */
case SSB_DEV_PCI:
case SSB_DEV_ETHERNET:
case SSB_DEV_ETHERNET_GBIT:
@ -281,6 +280,10 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore)
set_irq(dev, irq++);
break;
}
/* fallthrough */
case SSB_DEV_EXTIF:
set_irq(dev, 0);
break;
}
}
ssb_dprintk(KERN_INFO PFX "after irq reconfiguration\n");

View file

@ -494,8 +494,7 @@ static int ssb_devices_register(struct ssb_bus *bus)
#endif
break;
case SSB_BUSTYPE_SDIO:
#ifdef CONFIG_SSB_SDIO
sdev->irq = bus->host_sdio->dev.irq;
#ifdef CONFIG_SSB_SDIOHOST
dev->parent = &bus->host_sdio->dev;
#endif
break;

View file

@ -1561,37 +1561,82 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
* Documentation in Documentation/networking/radiotap-headers.txt
*/
struct radiotap_align_size {
uint8_t align:4, size:4;
};
struct ieee80211_radiotap_namespace {
const struct radiotap_align_size *align_size;
int n_bits;
uint32_t oui;
uint8_t subns;
};
struct ieee80211_radiotap_vendor_namespaces {
const struct ieee80211_radiotap_namespace *ns;
int n_ns;
};
/**
* struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args
* @rtheader: pointer to the radiotap header we are walking through
* @max_length: length of radiotap header in cpu byte ordering
* @this_arg_index: IEEE80211_RADIOTAP_... index of current arg
* @this_arg: pointer to current radiotap arg
* @arg_index: internal next argument index
* @arg: internal next argument pointer
* @next_bitmap: internal pointer to next present u32
* @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
* @this_arg_index: index of current arg, valid after each successful call
* to ieee80211_radiotap_iterator_next()
* @this_arg: pointer to current radiotap arg; it is valid after each
* call to ieee80211_radiotap_iterator_next() but also after
* ieee80211_radiotap_iterator_init() where it will point to
* the beginning of the actual data portion
* @this_arg_size: length of the current arg, for convenience
* @current_namespace: pointer to the current namespace definition
* (or internally %NULL if the current namespace is unknown)
* @is_radiotap_ns: indicates whether the current namespace is the default
* radiotap namespace or not
*
* @overrides: override standard radiotap fields
* @n_overrides: number of overrides
*
* @_rtheader: pointer to the radiotap header we are walking through
* @_max_length: length of radiotap header in cpu byte ordering
* @_arg_index: next argument index
* @_arg: next argument pointer
* @_next_bitmap: internal pointer to next present u32
* @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
* @_vns: vendor namespace definitions
* @_next_ns_data: beginning of the next namespace's data
* @_reset_on_ext: internal; reset the arg index to 0 when going to the
* next bitmap word
*
* Describes the radiotap parser state. Fields prefixed with an underscore
* must not be used by users of the parser, only by the parser internally.
*/
struct ieee80211_radiotap_iterator {
struct ieee80211_radiotap_header *rtheader;
int max_length;
int this_arg_index;
u8 *this_arg;
struct ieee80211_radiotap_header *_rtheader;
const struct ieee80211_radiotap_vendor_namespaces *_vns;
const struct ieee80211_radiotap_namespace *current_namespace;
int arg_index;
u8 *arg;
__le32 *next_bitmap;
u32 bitmap_shifter;
unsigned char *_arg, *_next_ns_data;
uint32_t *_next_bitmap;
unsigned char *this_arg;
int this_arg_index;
int this_arg_size;
int is_radiotap_ns;
int _max_length;
int _arg_index;
uint32_t _bitmap_shifter;
int _reset_on_ext;
};
extern int ieee80211_radiotap_iterator_init(
struct ieee80211_radiotap_iterator *iterator,
struct ieee80211_radiotap_header *radiotap_header,
int max_length);
struct ieee80211_radiotap_iterator *iterator,
struct ieee80211_radiotap_header *radiotap_header,
int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns);
extern int ieee80211_radiotap_iterator_next(
struct ieee80211_radiotap_iterator *iterator);
struct ieee80211_radiotap_iterator *iterator);
extern const unsigned char rfc1042_header[6];
extern const unsigned char bridge_tunnel_header[6];

View file

@ -198,6 +198,10 @@ enum ieee80211_radiotap_type {
IEEE80211_RADIOTAP_TX_FLAGS = 15,
IEEE80211_RADIOTAP_RTS_RETRIES = 16,
IEEE80211_RADIOTAP_DATA_RETRIES = 17,
/* valid in every it_present bitmap, even vendor namespaces */
IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30,
IEEE80211_RADIOTAP_EXT = 31
};

View file

@ -117,19 +117,6 @@ struct ieee80211_tx_queue_params {
bool uapsd;
};
/**
* struct ieee80211_tx_queue_stats - transmit queue statistics
*
* @len: number of packets in queue
* @limit: queue length limit
* @count: number of frames sent
*/
struct ieee80211_tx_queue_stats {
unsigned int len;
unsigned int limit;
unsigned int count;
};
struct ieee80211_low_level_stats {
unsigned int dot11ACKFailureCount;
unsigned int dot11RTSFailureCount;
@ -814,7 +801,7 @@ enum set_key_cmd {
* mac80211, any ieee80211_sta pointer you get access to must
* either be protected by rcu_read_lock() explicitly or implicitly,
* or you must take good care to not use such a pointer after a
* call to your sta_notify callback that removed it.
* call to your sta_remove callback that removed it.
*
* @addr: MAC address
* @aid: AID we assigned to the station if we're an AP
@ -840,8 +827,8 @@ struct ieee80211_sta {
* indicates addition and removal of a station to station table,
* or if a associated station made a power state transition.
*
* @STA_NOTIFY_ADD: a station was added to the station table
* @STA_NOTIFY_REMOVE: a station being removed from the station table
* @STA_NOTIFY_ADD: (DEPRECATED) a station was added to the station table
* @STA_NOTIFY_REMOVE: (DEPRECATED) a station being removed from the station table
* @STA_NOTIFY_SLEEP: a station is now sleeping
* @STA_NOTIFY_AWAKE: a sleeping station woke up
*/
@ -958,6 +945,11 @@ enum ieee80211_tkip_key_type {
* Hardware supports Unscheduled Automatic Power Save Delivery
* (U-APSD) in managed mode. The mode is configured with
* conf_tx() operation.
*
* @IEEE80211_HW_REPORTS_TX_ACK_STATUS:
* Hardware can provide ack status reports of Tx frames to
* the stack.
*
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@ -978,6 +970,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
};
/**
@ -1534,22 +1527,20 @@ enum ieee80211_ampdu_mlme_action {
* @set_rts_threshold: Configuration of RTS threshold (if device needs it)
* The callback can sleep.
*
* @sta_notify: Notifies low level driver about addition, removal or power
* state transition of an associated station, AP, IBSS/WDS/mesh peer etc.
* Must be atomic.
* @sta_add: Notifies low level driver about addition of an associated station,
* AP, IBSS/WDS/mesh peer etc. This callback can sleep.
*
* @sta_remove: Notifies low level driver about removal of an associated
* station, AP, IBSS/WDS/mesh peer etc. This callback can sleep.
*
* @sta_notify: Notifies low level driver about power state transition of an
* associated station, AP, IBSS/WDS/mesh peer etc. Must be atomic.
*
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
* bursting) for a hardware TX queue.
* Returns a negative error code on failure.
* The callback can sleep.
*
* @get_tx_stats: Get statistics of the current TX queue status. This is used
* to get number of currently queued packets (queue length), maximum queue
* size (limit), and total number of packets sent using each TX queue
* (count). The 'stats' pointer points to an array that has hw->queues
* items.
* The callback must be atomic.
*
* @get_tsf: Get the current TSF timer value from firmware/hardware. Currently,
* this is only used for IBSS mode BSSID merging and debugging. Is not a
* required function.
@ -1635,12 +1626,14 @@ struct ieee80211_ops {
void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx,
u32 *iv32, u16 *iv16);
int (*set_rts_threshold)(struct ieee80211_hw *hw, u32 value);
int (*sta_add)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int (*sta_remove)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum sta_notify_cmd, struct ieee80211_sta *sta);
int (*conf_tx)(struct ieee80211_hw *hw, u16 queue,
const struct ieee80211_tx_queue_params *params);
int (*get_tx_stats)(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats);
u64 (*get_tsf)(struct ieee80211_hw *hw);
void (*set_tsf)(struct ieee80211_hw *hw, u64 tsf);
void (*reset_tsf)(struct ieee80211_hw *hw);

View file

@ -237,6 +237,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
sdata->vif.type != NL80211_IFTYPE_AP)
return -EINVAL;
if (test_sta_flags(sta, WLAN_STA_DISASSOC)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Disassociation is in progress. "
"Denying BA session request\n");
#endif
return -EINVAL;
}
if (test_sta_flags(sta, WLAN_STA_SUSPEND)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Suspend in progress. "

View file

@ -515,6 +515,8 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
if (old)
memcpy(new->tail, old->tail, new_tail_len);
sdata->vif.bss_conf.dtim_period = new->dtim_period;
rcu_assign_pointer(sdata->u.ap.beacon, new);
synchronize_rcu();
@ -747,9 +749,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
sdata->vif.type == NL80211_IFTYPE_AP;
rcu_read_lock();
err = sta_info_insert(sta);
err = sta_info_insert_rcu(sta);
if (err) {
rcu_read_unlock();
return err;
@ -768,26 +768,13 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (mac) {
rcu_read_lock();
sta = sta_info_get_bss(sdata, mac);
if (!sta) {
rcu_read_unlock();
return -ENOENT;
}
sta_info_unlink(&sta);
rcu_read_unlock();
sta_info_destroy(sta);
} else
sta_info_flush(local, sdata);
if (mac)
return sta_info_destroy_addr_bss(sdata, mac);
sta_info_flush(local, sdata);
return 0;
}

View file

@ -250,6 +250,38 @@ static const struct file_operations uapsd_max_sp_len_ops = {
.open = mac80211_open_file_generic
};
static ssize_t channel_type_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
const char *buf;
switch (local->hw.conf.channel_type) {
case NL80211_CHAN_NO_HT:
buf = "no ht\n";
break;
case NL80211_CHAN_HT20:
buf = "ht20\n";
break;
case NL80211_CHAN_HT40MINUS:
buf = "ht40-\n";
break;
case NL80211_CHAN_HT40PLUS:
buf = "ht40+\n";
break;
default:
buf = "???";
break;
}
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
}
static const struct file_operations channel_type_ops = {
.read = channel_type_read,
.open = mac80211_open_file_generic
};
static ssize_t queues_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@ -408,6 +440,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(noack);
DEBUGFS_ADD(uapsd_queues);
DEBUGFS_ADD(uapsd_max_sp_len);
DEBUGFS_ADD(channel_type);
statsd = debugfs_create_dir("statistics", phyd);

View file

@ -243,6 +243,40 @@ static inline void drv_sta_notify(struct ieee80211_local *local,
trace_drv_sta_notify(local, sdata, cmd, sta);
}
static inline int drv_sta_add(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta)
{
int ret = 0;
might_sleep();
if (local->ops->sta_add)
ret = local->ops->sta_add(&local->hw, &sdata->vif, sta);
else if (local->ops->sta_notify)
local->ops->sta_notify(&local->hw, &sdata->vif,
STA_NOTIFY_ADD, sta);
trace_drv_sta_add(local, sdata, sta, ret);
return ret;
}
static inline void drv_sta_remove(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta)
{
might_sleep();
if (local->ops->sta_remove)
local->ops->sta_remove(&local->hw, &sdata->vif, sta);
else if (local->ops->sta_notify)
local->ops->sta_notify(&local->hw, &sdata->vif,
STA_NOTIFY_REMOVE, sta);
trace_drv_sta_remove(local, sdata, sta);
}
static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
@ -256,14 +290,6 @@ static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue,
return ret;
}
static inline int drv_get_tx_stats(struct ieee80211_local *local,
struct ieee80211_tx_queue_stats *stats)
{
int ret = local->ops->get_tx_stats(&local->hw, stats);
trace_drv_get_tx_stats(local, stats, ret);
return ret;
}
static inline u64 drv_get_tsf(struct ieee80211_local *local)
{
u64 ret = -1ULL;

View file

@ -545,6 +545,58 @@ TRACE_EVENT(drv_sta_notify,
)
);
TRACE_EVENT(drv_sta_add,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta, int ret),
TP_ARGS(local, sdata, sta, ret),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
STA_ENTRY
__field(int, ret)
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
STA_ASSIGN;
__entry->ret = ret;
),
TP_printk(
LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " ret:%d",
LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ret
)
);
TRACE_EVENT(drv_sta_remove,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta),
TP_ARGS(local, sdata, sta),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
STA_ENTRY
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
STA_ASSIGN;
),
TP_printk(
LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT,
LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG
)
);
TRACE_EVENT(drv_conf_tx,
TP_PROTO(struct ieee80211_local *local, u16 queue,
const struct ieee80211_tx_queue_params *params,
@ -578,29 +630,6 @@ TRACE_EVENT(drv_conf_tx,
)
);
TRACE_EVENT(drv_get_tx_stats,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_tx_queue_stats *stats,
int ret),
TP_ARGS(local, stats, ret),
TP_STRUCT__entry(
LOCAL_ENTRY
__field(int, ret)
),
TP_fast_assign(
LOCAL_ASSIGN;
__entry->ret = ret;
),
TP_printk(
LOCAL_PR_FMT " ret:%d",
LOCAL_PR_ARG, __entry->ret
)
);
TRACE_EVENT(drv_get_tsf,
TP_PROTO(struct ieee80211_local *local, u64 ret),

View file

@ -275,10 +275,12 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
(unsigned long long) supp_rates,
(unsigned long long) sta->sta.supp_rates[band]);
#endif
} else
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
rcu_read_unlock();
rcu_read_unlock();
} else {
rcu_read_unlock();
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
supp_rates, GFP_KERNEL);
}
}
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
@ -368,7 +370,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sdata->name, mgmt->bssid);
#endif
ieee80211_sta_join_ibss(sdata, bss);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
supp_rates, GFP_KERNEL);
}
put_bss:
@ -381,7 +384,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* must be callable in atomic context.
*/
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
u8 *bssid,u8 *addr, u32 supp_rates)
u8 *bssid,u8 *addr, u32 supp_rates,
gfp_t gfp)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
@ -410,7 +414,7 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
wiphy_name(local->hw.wiphy), addr, sdata->name);
#endif
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
sta = sta_info_alloc(sdata, addr, gfp);
if (!sta)
return NULL;
@ -422,9 +426,9 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
rate_control_rate_init(sta);
/* If it fails, maybe we raced another insertion? */
if (sta_info_insert(sta))
return NULL;
return sta_info_get(sdata, addr);
return sta;
}
@ -652,7 +656,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
}
if (pos[1] != 0 &&
(pos[1] != ifibss->ssid_len ||
!memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
/* Ignore ProbeReq for foreign SSID */
return;
}

View file

@ -316,6 +316,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_CSA_RECEIVED = BIT(5),
IEEE80211_STA_MFP_ENABLED = BIT(6),
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
IEEE80211_STA_NULLFUNC_ACKED = BIT(8),
};
struct ieee80211_if_managed {
@ -688,15 +689,18 @@ struct ieee80211_local {
/* Station data */
/*
* The lock only protects the list, hash, timer and counter
* against manipulation, reads are done in RCU. Additionally,
* the lock protects each BSS's TIM bitmap.
* The mutex only protects the list and counter,
* reads are done in RCU.
* Additionally, the lock protects the hash table,
* the pending list and each BSS's TIM bitmap.
*/
struct mutex sta_mtx;
spinlock_t sta_lock;
unsigned long num_sta;
struct list_head sta_list;
struct list_head sta_list, sta_pending_list;
struct sta_info *sta_hash[STA_HASH_SIZE];
struct timer_list sta_cleanup;
struct work_struct sta_finish_work;
int sta_generation;
struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
@ -770,10 +774,6 @@ struct ieee80211_local {
assoc_led_name[32], radio_led_name[32];
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
struct work_struct sta_debugfs_add;
#endif
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
/* TX/RX handler statistics */
unsigned int tx_handlers_drop;
@ -985,7 +985,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata);
ieee80211_rx_result
ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
u8 *bssid, u8 *addr, u32 supp_rates);
u8 *bssid, u8 *addr, u32 supp_rates,
gfp_t gfp);
int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
struct cfg80211_ibss_params *params);
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);

View file

@ -102,7 +102,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
if (local->num_sta >= MESH_MAX_PLINKS)
return NULL;
sta = sta_info_alloc(sdata, hw_addr, GFP_ATOMIC);
sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
if (!sta)
return NULL;
@ -236,12 +236,12 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data
sta = sta_info_get(sdata, hw_addr);
if (!sta) {
rcu_read_unlock();
sta = mesh_plink_alloc(sdata, hw_addr, rates);
if (!sta) {
rcu_read_unlock();
if (!sta)
return;
}
if (sta_info_insert(sta)) {
if (sta_info_insert_rcu(sta)) {
rcu_read_unlock();
return;
}
@ -485,9 +485,11 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
} else if (!sta) {
/* ftype == PLINK_OPEN */
u32 rates;
rcu_read_unlock();
if (!mesh_plink_free_count(sdata)) {
mpl_dbg("Mesh plink error: no more free plinks\n");
rcu_read_unlock();
return;
}
@ -495,10 +497,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta = mesh_plink_alloc(sdata, mgmt->sa, rates);
if (!sta) {
mpl_dbg("Mesh plink error: plink table full\n");
rcu_read_unlock();
return;
}
if (sta_info_insert(sta)) {
if (sta_info_insert_rcu(sta)) {
rcu_read_unlock();
return;
}

View file

@ -27,10 +27,6 @@
#include "rate.h"
#include "led.h"
#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
#define IEEE80211_AUTH_MAX_TRIES 3
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
#define IEEE80211_ASSOC_MAX_TRIES 3
#define IEEE80211_MAX_PROBE_TRIES 5
/*
@ -438,8 +434,11 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
} else {
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
ieee80211_send_nullfunc(local, sdata, 1);
conf->flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
conf->flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
}
}
@ -545,6 +544,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
container_of(work, struct ieee80211_local,
dynamic_ps_enable_work);
struct ieee80211_sub_if_data *sdata = local->ps_sdata;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
/* can only happen when PS was just disabled anyway */
if (!sdata)
@ -553,11 +553,16 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
if (local->hw.conf.flags & IEEE80211_CONF_PS)
return;
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
(!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)))
ieee80211_send_nullfunc(local, sdata, 1);
local->hw.conf.flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) ||
(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
local->hw.conf.flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
}
void ieee80211_dynamic_ps_timer(unsigned long data)
@ -792,8 +797,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
rcu_read_lock();
sta = sta_info_get(sdata, bssid);
if (sta)
if (sta) {
set_sta_flags(sta, WLAN_STA_DISASSOC);
ieee80211_sta_tear_down_BA_sessions(sta);
}
rcu_read_unlock();
changed |= ieee80211_reset_erp_info(sdata);
@ -826,19 +833,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
changed |= BSS_CHANGED_BSSID;
ieee80211_bss_info_change_notify(sdata, changed);
rcu_read_lock();
sta = sta_info_get(sdata, bssid);
if (!sta) {
rcu_read_unlock();
return;
}
sta_info_unlink(&sta);
rcu_read_unlock();
sta_info_destroy(sta);
sta_info_destroy_addr(sdata, bssid);
}
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@ -1844,7 +1839,11 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
wk->probe_auth.algorithm = auth_alg;
wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
wk->type = IEEE80211_WORK_DIRECT_PROBE;
/* if we already have a probe, don't probe again */
if (req->bss->proberesp_ies)
wk->type = IEEE80211_WORK_AUTH;
else
wk->type = IEEE80211_WORK_DIRECT_PROBE;
wk->chan = req->bss->channel;
wk->sdata = sdata;
wk->done = ieee80211_probe_auth_done;
@ -1904,6 +1903,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
return -ENOMEM;
ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
@ -2007,12 +2007,18 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
mutex_lock(&local->work_mtx);
list_for_each_entry(wk, &local->work_list, list) {
if (wk->type != IEEE80211_WORK_DIRECT_PROBE)
if (wk->sdata != sdata)
continue;
if (wk->type != IEEE80211_WORK_DIRECT_PROBE &&
wk->type != IEEE80211_WORK_AUTH)
continue;
if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN))
continue;
not_auth_yet = true;
list_del(&wk->list);
not_auth_yet = wk->type == IEEE80211_WORK_DIRECT_PROBE;
list_del_rcu(&wk->list);
free_work(wk);
break;
}

View file

@ -11,7 +11,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
unsigned long flags;
ieee80211_scan_cancel(local);
@ -55,22 +54,21 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
rcu_read_unlock();
/* remove STAs */
spin_lock_irqsave(&local->sta_lock, flags);
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
if (local->ops->sta_notify) {
if (sta->uploaded) {
sdata = sta->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
u.ap);
drv_sta_notify(local, sdata, STA_NOTIFY_REMOVE,
&sta->sta);
drv_sta_remove(local, sdata, &sta->sta);
}
mesh_plink_quiesce(sta);
}
spin_unlock_irqrestore(&local->sta_lock, flags);
mutex_unlock(&local->sta_mtx);
/* remove all interfaces */
list_for_each_entry(sdata, &local->interfaces, list) {

View file

@ -145,7 +145,7 @@ static const struct file_operations rcname_ops = {
};
#endif
struct rate_control_ref *rate_control_alloc(const char *name,
static struct rate_control_ref *rate_control_alloc(const char *name,
struct ieee80211_local *local)
{
struct dentry *debugfsdir = NULL;

View file

@ -26,10 +26,6 @@ struct rate_control_ref {
struct kref kref;
};
/* Get a reference to the rate control algorithm. If `name' is NULL, get the
* first available algorithm. */
struct rate_control_ref *rate_control_alloc(const char *name,
struct ieee80211_local *local);
void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_tx_rate_control *txrc);
@ -116,7 +112,8 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta)
#endif
}
/* functions for rate control related to a device */
/* Get a reference to the rate control algorithm. If `name' is NULL, get the
* first available algorithm. */
int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
const char *name);
void rate_control_deinitialize(struct ieee80211_local *local);

View file

@ -1719,6 +1719,7 @@ static ieee80211_rx_result debug_noinline
ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
{
struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_local *local = rx->local;
struct net_device *dev = sdata->dev;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
__le16 fc = hdr->frame_control;
@ -1750,6 +1751,13 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
dev->stats.rx_packets++;
dev->stats.rx_bytes += rx->skb->len;
if (ieee80211_is_data(hdr->frame_control) &&
!is_multicast_ether_addr(hdr->addr1) &&
local->hw.conf.dynamic_ps_timeout > 0 && local->ps_sdata) {
mod_timer(&local->dynamic_ps_timer, jiffies +
msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
}
ieee80211_deliver_skb(rx);
return RX_QUEUED;
@ -2244,8 +2252,8 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
rate_idx = 0; /* TODO: HT rates */
else
rate_idx = status->rate_idx;
rx->sta = ieee80211_ibss_add_sta(sdata, bssid, hdr->addr2,
BIT(rate_idx));
rx->sta = ieee80211_ibss_add_sta(sdata, bssid,
hdr->addr2, BIT(rate_idx), GFP_ATOMIC);
}
break;
case NL80211_IFTYPE_MESH_POINT:

View file

@ -345,6 +345,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->scan_req)
return -EBUSY;
if (!list_empty(&local->work_list)) {
/* wait for the work to finish/time out */
local->scan_req = req;
local->scan_sdata = sdata;
return 0;
}
if (local->ops->hw_scan) {
u8 *ies;
@ -364,29 +371,33 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
local->hw_scan_req->ie = ies;
local->hw_scan_band = 0;
/*
* After allocating local->hw_scan_req, we must
* go through until ieee80211_prep_hw_scan(), so
* anything that might be changed here and leave
* this function early must not go after this
* allocation.
*/
}
local->scan_req = req;
local->scan_sdata = sdata;
if (!list_empty(&local->work_list)) {
/* wait for the work to finish/time out */
return 0;
}
if (local->ops->hw_scan)
__set_bit(SCAN_HW_SCANNING, &local->scanning);
else
__set_bit(SCAN_SW_SCANNING, &local->scanning);
/*
* Kicking off the scan need not be protected,
* only the scan variable stuff, since now
* local->scan_req is assigned and other callers
* will abort their scan attempts.
*
* This avoids getting a scan_mtx -> iflist_mtx
* dependency, so that the scan completed calls
* have more locking freedom.
* This avoids too many locking dependencies
* so that the scan completed calls have more
* locking freedom.
*/
ieee80211_recalc_idle(local);

View file

@ -32,49 +32,33 @@
* for faster lookup and a list for iteration. They are managed using
* RCU, i.e. access to the list and hash table is protected by RCU.
*
* Upon allocating a STA info structure with sta_info_alloc(), the caller owns
* that structure. It must then either destroy it using sta_info_destroy()
* (which is pretty useless) or insert it into the hash table using
* sta_info_insert() which demotes the reference from ownership to a regular
* RCU-protected reference; if the function is called without protection by an
* RCU critical section the reference is instantly invalidated. Note that the
* caller may not do much with the STA info before inserting it, in particular,
* it may not start any mesh peer link management or add encryption keys.
* Upon allocating a STA info structure with sta_info_alloc(), the caller
* owns that structure. It must then insert it into the hash table using
* either sta_info_insert() or sta_info_insert_rcu(); only in the latter
* case (which acquires an rcu read section but must not be called from
* within one) will the pointer still be valid after the call. Note that
* the caller may not do much with the STA info before inserting it, in
* particular, it may not start any mesh peer link management or add
* encryption keys.
*
* When the insertion fails (sta_info_insert()) returns non-zero), the
* structure will have been freed by sta_info_insert()!
*
* sta entries are added by mac80211 when you establish a link with a
* Station entries are added by mac80211 when you establish a link with a
* peer. This means different things for the different type of interfaces
* we support. For a regular station this mean we add the AP sta when we
* receive an assocation response from the AP. For IBSS this occurs when
* we receive a probe response or a beacon from target IBSS network. For
* WDS we add the sta for the peer imediately upon device open. When using
* AP mode we add stations for each respective station upon request from
* userspace through nl80211.
* get to know about a peer on the same IBSS. For WDS we add the sta for
* the peer imediately upon device open. When using AP mode we add stations
* for each respective station upon request from userspace through nl80211.
*
* Because there are debugfs entries for each station, and adding those
* must be able to sleep, it is also possible to "pin" a station entry,
* that means it can be removed from the hash table but not be freed.
* See the comment in __sta_info_unlink() for more information, this is
* an internal capability only.
* In order to remove a STA info structure, various sta_info_destroy_*()
* calls are available.
*
* In order to remove a STA info structure, the caller needs to first
* unlink it (sta_info_unlink()) from the list and hash tables and
* then destroy it; sta_info_destroy() will wait for an RCU grace period
* to elapse before actually freeing it. Due to the pinning and the
* possibility of multiple callers trying to remove the same STA info at
* the same time, sta_info_unlink() can clear the STA info pointer it is
* passed to indicate that the STA info is owned by somebody else now.
*
* If sta_info_unlink() did not clear the pointer then the caller owns
* the STA info structure now and is responsible of destroying it with
* a call to sta_info_destroy().
*
* In all other cases, there is no concept of ownership on a STA entry,
* each structure is owned by the global hash table/list until it is
* removed. All users of the structure need to be RCU protected so that
* the structure won't be freed before they are done using it.
* There is no concept of ownership on a STA entry, each structure is
* owned by the global hash table/list until it is removed. All users of
* the structure need to be RCU protected so that the structure won't be
* freed before they are done using it.
*/
/* Caller must hold local->sta_lock */
@ -185,101 +169,6 @@ static void __sta_info_free(struct ieee80211_local *local,
kfree(sta);
}
void sta_info_destroy(struct sta_info *sta)
{
struct ieee80211_local *local;
struct sk_buff *skb;
int i;
might_sleep();
if (!sta)
return;
local = sta->local;
cancel_work_sync(&sta->drv_unblock_wk);
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
#ifdef CONFIG_MAC80211_MESH
if (ieee80211_vif_is_mesh(&sta->sdata->vif))
mesh_plink_deactivate(sta);
#endif
/*
* We have only unlinked the key, and actually destroying it
* may mean it is removed from hardware which requires that
* the key->sta pointer is still valid, so flush the key todo
* list here.
*
* ieee80211_key_todo() will synchronize_rcu() so after this
* nothing can reference this sta struct any more.
*/
ieee80211_key_todo();
#ifdef CONFIG_MAC80211_MESH
if (ieee80211_vif_is_mesh(&sta->sdata->vif))
del_timer_sync(&sta->plink_timer);
#endif
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
local->total_ps_buffered--;
dev_kfree_skb_any(skb);
}
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
dev_kfree_skb_any(skb);
for (i = 0; i < STA_TID_NUM; i++) {
struct tid_ampdu_rx *tid_rx;
struct tid_ampdu_tx *tid_tx;
spin_lock_bh(&sta->lock);
tid_rx = sta->ampdu_mlme.tid_rx[i];
/* Make sure timer won't free the tid_rx struct, see below */
if (tid_rx)
tid_rx->shutdown = true;
spin_unlock_bh(&sta->lock);
/*
* Outside spinlock - shutdown is true now so that the timer
* won't free tid_rx, we have to do that now. Can't let the
* timer do it because we have to sync the timer outside the
* lock that it takes itself.
*/
if (tid_rx) {
del_timer_sync(&tid_rx->session_timer);
kfree(tid_rx);
}
/*
* No need to do such complications for TX agg sessions, the
* path leading to freeing the tid_tx struct goes via a call
* from the driver, and thus needs to look up the sta struct
* again, which cannot be found when we get here. Hence, we
* just need to delete the timer and free the aggregation
* info; we won't be telling the peer about it then but that
* doesn't matter if we're not talking to it again anyway.
*/
tid_tx = sta->ampdu_mlme.tid_tx[i];
if (tid_tx) {
del_timer_sync(&tid_tx->addba_resp_timer);
/*
* STA removed while aggregation session being
* started? Bit odd, but purge frames anyway.
*/
skb_queue_purge(&tid_tx->pending);
kfree(tid_tx);
}
}
__sta_info_free(local, sta);
}
/* Caller must hold local->sta_lock */
static void sta_info_hash_add(struct ieee80211_local *local,
struct sta_info *sta)
@ -376,7 +265,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
return sta;
}
int sta_info_insert(struct sta_info *sta)
static int sta_info_finish_insert(struct sta_info *sta, bool async)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
@ -384,6 +273,91 @@ int sta_info_insert(struct sta_info *sta)
unsigned long flags;
int err = 0;
WARN_ON(!mutex_is_locked(&local->sta_mtx));
/* notify driver */
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
u.ap);
err = drv_sta_add(local, sdata, &sta->sta);
if (err) {
if (!async)
return err;
printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to driver (%d)"
" - keeping it anyway.\n",
sdata->name, sta->sta.addr, err);
} else {
sta->uploaded = true;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (async)
printk(KERN_DEBUG "%s: Finished adding IBSS STA %pM\n",
wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif
}
sdata = sta->sdata;
if (!async) {
local->num_sta++;
local->sta_generation++;
smp_mb();
/* make the station visible */
spin_lock_irqsave(&local->sta_lock, flags);
sta_info_hash_add(local, sta);
spin_unlock_irqrestore(&local->sta_lock, flags);
}
list_add(&sta->list, &local->sta_list);
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
sinfo.filled = 0;
sinfo.generation = local->sta_generation;
cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
return 0;
}
static void sta_info_finish_pending(struct ieee80211_local *local)
{
struct sta_info *sta;
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
while (!list_empty(&local->sta_pending_list)) {
sta = list_first_entry(&local->sta_pending_list,
struct sta_info, list);
list_del(&sta->list);
spin_unlock_irqrestore(&local->sta_lock, flags);
sta_info_finish_insert(sta, true);
spin_lock_irqsave(&local->sta_lock, flags);
}
spin_unlock_irqrestore(&local->sta_lock, flags);
}
static void sta_info_finish_work(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, sta_finish_work);
mutex_lock(&local->sta_mtx);
sta_info_finish_pending(local);
mutex_unlock(&local->sta_mtx);
}
int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
unsigned long flags;
int err = 0;
/*
* Can't be a WARN_ON because it can be triggered through a race:
* something inserts a STA (on one CPU) without holding the RTNL
@ -391,36 +365,87 @@ int sta_info_insert(struct sta_info *sta)
*/
if (unlikely(!ieee80211_sdata_running(sdata))) {
err = -ENETDOWN;
rcu_read_lock();
goto out_free;
}
if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 ||
is_multicast_ether_addr(sta->sta.addr))) {
err = -EINVAL;
rcu_read_lock();
goto out_free;
}
/*
* In ad-hoc mode, we sometimes need to insert stations
* from tasklet context from the RX path. To avoid races,
* always do so in that case -- see the comment below.
*/
if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
spin_lock_irqsave(&local->sta_lock, flags);
/* check if STA exists already */
if (sta_info_get_bss(sdata, sta->sta.addr)) {
spin_unlock_irqrestore(&local->sta_lock, flags);
rcu_read_lock();
err = -EEXIST;
goto out_free;
}
local->num_sta++;
local->sta_generation++;
smp_mb();
sta_info_hash_add(local, sta);
list_add_tail(&sta->list, &local->sta_pending_list);
rcu_read_lock();
spin_unlock_irqrestore(&local->sta_lock, flags);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Added IBSS STA %pM\n",
wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
ieee80211_queue_work(&local->hw, &local->sta_finish_work);
return 0;
}
/*
* On first glance, this will look racy, because the code
* below this point, which inserts a station with sleeping,
* unlocks the sta_lock between checking existence in the
* hash table and inserting into it.
*
* However, it is not racy against itself because it keeps
* the mutex locked. It still seems to race against the
* above code that atomically inserts the station... That,
* however, is not true because the above code can only
* be invoked for IBSS interfaces, and the below code will
* not be -- and the two do not race against each other as
* the hash table also keys off the interface.
*/
might_sleep();
mutex_lock(&local->sta_mtx);
spin_lock_irqsave(&local->sta_lock, flags);
/* check if STA exists already */
if (sta_info_get(sdata, sta->sta.addr)) {
if (sta_info_get_bss(sdata, sta->sta.addr)) {
spin_unlock_irqrestore(&local->sta_lock, flags);
rcu_read_lock();
err = -EEXIST;
goto out_free;
}
list_add(&sta->list, &local->sta_list);
local->sta_generation++;
local->num_sta++;
sta_info_hash_add(local, sta);
/* notify driver */
if (local->ops->sta_notify) {
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
u.ap);
spin_unlock_irqrestore(&local->sta_lock, flags);
drv_sta_notify(local, sdata, STA_NOTIFY_ADD, &sta->sta);
sdata = sta->sdata;
err = sta_info_finish_insert(sta, false);
if (err) {
mutex_unlock(&local->sta_mtx);
rcu_read_lock();
goto out_free;
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@ -428,22 +453,9 @@ int sta_info_insert(struct sta_info *sta)
wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
spin_unlock_irqrestore(&local->sta_lock, flags);
sinfo.filled = 0;
sinfo.generation = local->sta_generation;
cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_ATOMIC);
#ifdef CONFIG_MAC80211_DEBUGFS
/*
* Debugfs entry adding might sleep, so schedule process
* context task for adding entry for STAs that do not yet
* have one.
* NOTE: due to auto-freeing semantics this may only be done
* if the insertion is successful!
*/
schedule_work(&local->sta_debugfs_add);
#endif
/* move reference to rcu-protected */
rcu_read_lock();
mutex_unlock(&local->sta_mtx);
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_accept_plinks_update(sdata);
@ -455,6 +467,15 @@ int sta_info_insert(struct sta_info *sta)
return err;
}
int sta_info_insert(struct sta_info *sta)
{
int err = sta_info_insert_rcu(sta);
rcu_read_unlock();
return err;
}
static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
{
/*
@ -523,108 +544,6 @@ void sta_info_clear_tim_bit(struct sta_info *sta)
spin_unlock_irqrestore(&sta->local->sta_lock, flags);
}
static void __sta_info_unlink(struct sta_info **sta)
{
struct ieee80211_local *local = (*sta)->local;
struct ieee80211_sub_if_data *sdata = (*sta)->sdata;
/*
* pull caller's reference if we're already gone.
*/
if (sta_info_hash_del(local, *sta)) {
*sta = NULL;
return;
}
if ((*sta)->key) {
ieee80211_key_free((*sta)->key);
WARN_ON((*sta)->key);
}
list_del(&(*sta)->list);
(*sta)->dead = true;
if (test_and_clear_sta_flags(*sta,
WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
BUG_ON(!sdata->bss);
atomic_dec(&sdata->bss->num_sta_ps);
__sta_info_clear_tim_bit(sdata->bss, *sta);
}
local->num_sta--;
local->sta_generation++;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
rcu_assign_pointer(sdata->u.vlan.sta, NULL);
if (local->ops->sta_notify) {
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
u.ap);
drv_sta_notify(local, sdata, STA_NOTIFY_REMOVE,
&(*sta)->sta);
sdata = (*sta)->sdata;
}
if (ieee80211_vif_is_mesh(&sdata->vif)) {
mesh_accept_plinks_update(sdata);
#ifdef CONFIG_MAC80211_MESH
del_timer(&(*sta)->plink_timer);
#endif
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Removed STA %pM\n",
wiphy_name(local->hw.wiphy), (*sta)->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
/*
* Finally, pull caller's reference if the STA is pinned by the
* task that is adding the debugfs entries. In that case, we
* leave the STA "to be freed".
*
* The rules are not trivial, but not too complex either:
* (1) pin_status is only modified under the sta_lock
* (2) STAs may only be pinned under the RTNL so that
* sta_info_flush() is guaranteed to actually destroy
* all STAs that are active for a given interface, this
* is required for correctness because otherwise we
* could notify a driver that an interface is going
* away and only after that (!) notify it about a STA
* on that interface going away.
* (3) sta_info_debugfs_add_work() will set the status
* to PINNED when it found an item that needs a new
* debugfs directory created. In that case, that item
* must not be freed although all *RCU* users are done
* with it. Hence, we tell the caller of _unlink()
* that the item is already gone (as can happen when
* two tasks try to unlink/destroy at the same time)
* (4) We set the pin_status to DESTROY here when we
* find such an item.
* (5) sta_info_debugfs_add_work() will reset the pin_status
* from PINNED to NORMAL when it is done with the item,
* but will check for DESTROY before resetting it in
* which case it will free the item.
*/
if ((*sta)->pin_status == STA_INFO_PIN_STAT_PINNED) {
(*sta)->pin_status = STA_INFO_PIN_STAT_DESTROY;
*sta = NULL;
return;
}
}
void sta_info_unlink(struct sta_info **sta)
{
struct ieee80211_local *local = (*sta)->local;
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
__sta_info_unlink(sta);
spin_unlock_irqrestore(&local->sta_lock, flags);
}
static int sta_info_buffer_expired(struct sta_info *sta,
struct sk_buff *skb)
{
@ -681,6 +600,178 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
}
}
static int __must_check __sta_info_destroy(struct sta_info *sta)
{
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
struct sk_buff *skb;
unsigned long flags;
int ret, i;
might_sleep();
if (!sta)
return -ENOENT;
local = sta->local;
sdata = sta->sdata;
spin_lock_irqsave(&local->sta_lock, flags);
ret = sta_info_hash_del(local, sta);
/* this might still be the pending list ... which is fine */
if (!ret)
list_del(&sta->list);
spin_unlock_irqrestore(&local->sta_lock, flags);
if (ret)
return ret;
if (sta->key) {
ieee80211_key_free(sta->key);
/*
* We have only unlinked the key, and actually destroying it
* may mean it is removed from hardware which requires that
* the key->sta pointer is still valid, so flush the key todo
* list here.
*
* ieee80211_key_todo() will synchronize_rcu() so after this
* nothing can reference this sta struct any more.
*/
ieee80211_key_todo();
WARN_ON(sta->key);
}
sta->dead = true;
if (test_and_clear_sta_flags(sta,
WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
BUG_ON(!sdata->bss);
atomic_dec(&sdata->bss->num_sta_ps);
__sta_info_clear_tim_bit(sdata->bss, sta);
}
local->num_sta--;
local->sta_generation++;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
rcu_assign_pointer(sdata->u.vlan.sta, NULL);
if (sta->uploaded) {
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
u.ap);
drv_sta_remove(local, sdata, &sta->sta);
sdata = sta->sdata;
}
#ifdef CONFIG_MAC80211_MESH
if (ieee80211_vif_is_mesh(&sdata->vif)) {
mesh_accept_plinks_update(sdata);
del_timer(&sta->plink_timer);
}
#endif
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Removed STA %pM\n",
wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
cancel_work_sync(&sta->drv_unblock_wk);
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
#ifdef CONFIG_MAC80211_MESH
if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
mesh_plink_deactivate(sta);
del_timer_sync(&sta->plink_timer);
}
#endif
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
local->total_ps_buffered--;
dev_kfree_skb_any(skb);
}
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
dev_kfree_skb_any(skb);
for (i = 0; i < STA_TID_NUM; i++) {
struct tid_ampdu_rx *tid_rx;
struct tid_ampdu_tx *tid_tx;
spin_lock_bh(&sta->lock);
tid_rx = sta->ampdu_mlme.tid_rx[i];
/* Make sure timer won't free the tid_rx struct, see below */
if (tid_rx)
tid_rx->shutdown = true;
spin_unlock_bh(&sta->lock);
/*
* Outside spinlock - shutdown is true now so that the timer
* won't free tid_rx, we have to do that now. Can't let the
* timer do it because we have to sync the timer outside the
* lock that it takes itself.
*/
if (tid_rx) {
del_timer_sync(&tid_rx->session_timer);
kfree(tid_rx);
}
/*
* No need to do such complications for TX agg sessions, the
* path leading to freeing the tid_tx struct goes via a call
* from the driver, and thus needs to look up the sta struct
* again, which cannot be found when we get here. Hence, we
* just need to delete the timer and free the aggregation
* info; we won't be telling the peer about it then but that
* doesn't matter if we're not talking to it again anyway.
*/
tid_tx = sta->ampdu_mlme.tid_tx[i];
if (tid_tx) {
del_timer_sync(&tid_tx->addba_resp_timer);
/*
* STA removed while aggregation session being
* started? Bit odd, but purge frames anyway.
*/
skb_queue_purge(&tid_tx->pending);
kfree(tid_tx);
}
}
__sta_info_free(local, sta);
return 0;
}
int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr)
{
struct sta_info *sta;
int ret;
mutex_lock(&sdata->local->sta_mtx);
sta = sta_info_get(sdata, addr);
ret = __sta_info_destroy(sta);
mutex_unlock(&sdata->local->sta_mtx);
return ret;
}
int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
const u8 *addr)
{
struct sta_info *sta;
int ret;
mutex_lock(&sdata->local->sta_mtx);
sta = sta_info_get_bss(sdata, addr);
ret = __sta_info_destroy(sta);
mutex_unlock(&sdata->local->sta_mtx);
return ret;
}
static void sta_info_cleanup(unsigned long data)
{
@ -700,90 +791,18 @@ static void sta_info_cleanup(unsigned long data)
add_timer(&local->sta_cleanup);
}
#ifdef CONFIG_MAC80211_DEBUGFS
/*
* See comment in __sta_info_unlink,
* caller must hold local->sta_lock.
*/
static void __sta_info_pin(struct sta_info *sta)
{
WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_NORMAL);
sta->pin_status = STA_INFO_PIN_STAT_PINNED;
}
/*
* See comment in __sta_info_unlink, returns sta if it
* needs to be destroyed.
*/
static struct sta_info *__sta_info_unpin(struct sta_info *sta)
{
struct sta_info *ret = NULL;
unsigned long flags;
spin_lock_irqsave(&sta->local->sta_lock, flags);
WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_DESTROY &&
sta->pin_status != STA_INFO_PIN_STAT_PINNED);
if (sta->pin_status == STA_INFO_PIN_STAT_DESTROY)
ret = sta;
sta->pin_status = STA_INFO_PIN_STAT_NORMAL;
spin_unlock_irqrestore(&sta->local->sta_lock, flags);
return ret;
}
static void sta_info_debugfs_add_work(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, sta_debugfs_add);
struct sta_info *sta, *tmp;
unsigned long flags;
/* We need to keep the RTNL across the whole pinned status. */
rtnl_lock();
while (1) {
sta = NULL;
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry(tmp, &local->sta_list, list) {
/*
* debugfs.add_has_run will be set by
* ieee80211_sta_debugfs_add regardless
* of what else it does.
*/
if (!tmp->debugfs.add_has_run) {
sta = tmp;
__sta_info_pin(sta);
break;
}
}
spin_unlock_irqrestore(&local->sta_lock, flags);
if (!sta)
break;
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
sta = __sta_info_unpin(sta);
sta_info_destroy(sta);
}
rtnl_unlock();
}
#endif
void sta_info_init(struct ieee80211_local *local)
{
spin_lock_init(&local->sta_lock);
mutex_init(&local->sta_mtx);
INIT_LIST_HEAD(&local->sta_list);
INIT_LIST_HEAD(&local->sta_pending_list);
INIT_WORK(&local->sta_finish_work, sta_info_finish_work);
setup_timer(&local->sta_cleanup, sta_info_cleanup,
(unsigned long)local);
local->sta_cleanup.expires =
round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
#ifdef CONFIG_MAC80211_DEBUGFS
INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_work);
#endif
}
int sta_info_start(struct ieee80211_local *local)
@ -795,16 +814,6 @@ int sta_info_start(struct ieee80211_local *local)
void sta_info_stop(struct ieee80211_local *local)
{
del_timer(&local->sta_cleanup);
#ifdef CONFIG_MAC80211_DEBUGFS
/*
* Make sure the debugfs adding work isn't pending after this
* because we're about to be destroyed. It doesn't matter
* whether it ran or not since we're going to flush all STAs
* anyway.
*/
cancel_work_sync(&local->sta_debugfs_add);
#endif
sta_info_flush(local, NULL);
}
@ -820,26 +829,19 @@ int sta_info_flush(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
struct sta_info *sta, *tmp;
LIST_HEAD(tmp_list);
int ret = 0;
unsigned long flags;
might_sleep();
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
if (!sdata || sdata == sta->sdata) {
__sta_info_unlink(&sta);
if (sta) {
list_add_tail(&sta->list, &tmp_list);
ret++;
}
}
}
spin_unlock_irqrestore(&local->sta_lock, flags);
mutex_lock(&local->sta_mtx);
list_for_each_entry_safe(sta, tmp, &tmp_list, list)
sta_info_destroy(sta);
sta_info_finish_pending(local);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
if (!sdata || sdata == sta->sdata)
WARN_ON(__sta_info_destroy(sta));
}
mutex_unlock(&local->sta_mtx);
return ret;
}
@ -849,24 +851,17 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta, *tmp;
LIST_HEAD(tmp_list);
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
mutex_lock(&local->sta_mtx);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
if (time_after(jiffies, sta->last_rx + exp_time)) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: expiring inactive STA %pM\n",
sdata->name, sta->sta.addr);
#endif
__sta_info_unlink(&sta);
if (sta)
list_add(&sta->list, &tmp_list);
WARN_ON(__sta_info_destroy(sta));
}
spin_unlock_irqrestore(&local->sta_lock, flags);
list_for_each_entry_safe(sta, tmp, &tmp_list, list)
sta_info_destroy(sta);
mutex_unlock(&local->sta_mtx);
}
struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw,

View file

@ -42,6 +42,9 @@
* be in the queues
* @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping
* station in power-save mode, reply when the driver unblocks.
* @WLAN_STA_DISASSOC: Disassociation in progress.
* This is used to reject TX BA session requests when disassociation
* is in progress.
*/
enum ieee80211_sta_info_flags {
WLAN_STA_AUTH = 1<<0,
@ -57,6 +60,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_SUSPEND = 1<<11,
WLAN_STA_PS_DRIVER = 1<<12,
WLAN_STA_PSPOLL = 1<<13,
WLAN_STA_DISASSOC = 1<<14,
};
#define STA_TID_NUM 16
@ -162,11 +166,6 @@ struct sta_ampdu_mlme {
};
/* see __sta_info_unlink */
#define STA_INFO_PIN_STAT_NORMAL 0
#define STA_INFO_PIN_STAT_PINNED 1
#define STA_INFO_PIN_STAT_DESTROY 2
/**
* struct sta_info - STA information
*
@ -187,7 +186,6 @@ struct sta_ampdu_mlme {
* @flaglock: spinlock for flags accesses
* @drv_unblock_wk: used for driver PS unblocking
* @listen_interval: listen interval of this station, when we're acting as AP
* @pin_status: used internally for pinning a STA struct into memory
* @flags: STA flags, see &enum ieee80211_sta_info_flags
* @ps_tx_buf: buffer of frames to transmit to this station
* when it leaves power saving state
@ -226,6 +224,7 @@ struct sta_ampdu_mlme {
* @debugfs: debug filesystem info
* @sta: station information we share with the driver
* @dead: set to true when sta is unlinked
* @uploaded: set to true when sta is uploaded to the driver
*/
struct sta_info {
/* General information, mostly static */
@ -245,11 +244,7 @@ struct sta_info {
bool dead;
/*
* for use by the internal lifetime management,
* see __sta_info_unlink
*/
u8 pin_status;
bool uploaded;
/*
* frequently updated, locked with own spinlock (flaglock),
@ -449,18 +444,19 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
* Insert STA info into hash table/list, returns zero or a
* -EEXIST if (if the same MAC address is already present).
*
* Calling this without RCU protection makes the caller
* relinquish its reference to @sta.
* Calling the non-rcu version makes the caller relinquish,
* the _rcu version calls read_lock_rcu() and must be called
* without it held.
*/
int sta_info_insert(struct sta_info *sta);
/*
* Unlink a STA info from the hash table/list.
* This can NULL the STA pointer if somebody else
* has already unlinked it.
*/
void sta_info_unlink(struct sta_info **sta);
int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU);
int sta_info_insert_atomic(struct sta_info *sta);
int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata,
const u8 *addr);
int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
const u8 *addr);
void sta_info_destroy(struct sta_info *sta);
void sta_info_set_tim_bit(struct sta_info *sta);
void sta_info_clear_tim_bit(struct sta_info *sta);

View file

@ -188,6 +188,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
rcu_read_lock();
sband = local->hw.wiphy->bands[info->band];
fc = hdr->frame_control;
for_each_sta_info(local, hdr->addr1, sta, tmp) {
/* skip wrong virtual interface */
@ -205,8 +206,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}
fc = hdr->frame_control;
if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
(ieee80211_is_data_qos(fc))) {
u16 tid, ssn;
@ -275,6 +274,20 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
local->dot11FailedCount++;
}
if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc) &&
(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
local->ps_sdata && !(local->scanning)) {
if (info->flags & IEEE80211_TX_STAT_ACK) {
local->ps_sdata->u.mgd.flags |=
IEEE80211_STA_NULLFUNC_ACKED;
ieee80211_queue_work(&local->hw,
&local->dynamic_ps_enable_work);
} else
mod_timer(&local->dynamic_ps_timer, jiffies +
msecs_to_jiffies(10));
}
/* this was a transmitted frame, but now we want to reuse it */
skb_orphan(skb);

View file

@ -571,7 +571,7 @@ ieee80211_tx_h_sta(struct ieee80211_tx_data *tx)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
if (tx->sta)
if (tx->sta && tx->sta->uploaded)
info->control.sta = &tx->sta->sta;
return TX_CONTINUE;
@ -1010,7 +1010,8 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
(struct ieee80211_radiotap_header *) skb->data;
struct ieee80211_supported_band *sband;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
NULL);
sband = tx->local->hw.wiphy->bands[tx->channel->band];
@ -1046,7 +1047,7 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
* because it will be recomputed and added
* on transmission
*/
if (skb->len < (iterator.max_length + FCS_LEN))
if (skb->len < (iterator._max_length + FCS_LEN))
return false;
skb_trim(skb, skb->len - FCS_LEN);
@ -1073,10 +1074,10 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
/*
* remove the radiotap header
* iterator->max_length was sanity-checked against
* iterator->_max_length was sanity-checked against
* skb->len by iterator init
*/
skb_pull(skb, iterator.max_length);
skb_pull(skb, iterator._max_length);
return true;
}

View file

@ -1082,7 +1082,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
struct ieee80211_hw *hw = &local->hw;
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
unsigned long flags;
int res;
if (local->suspended)
@ -1116,20 +1115,19 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
/* add STAs back */
if (local->ops->sta_notify) {
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry(sta, &local->sta_list, list) {
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
if (sta->uploaded) {
sdata = sta->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
u.ap);
drv_sta_notify(local, sdata, STA_NOTIFY_ADD,
&sta->sta);
WARN_ON(drv_sta_add(local, sdata, &sta->sta));
}
spin_unlock_irqrestore(&local->sta_lock, flags);
}
mutex_unlock(&local->sta_mtx);
/* Clear Suspend state so that ADDBA requests can be processed */
@ -1180,6 +1178,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
}
rcu_read_lock();
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
list_for_each_entry_rcu(sta, &local->sta_list, list) {
ieee80211_sta_tear_down_BA_sessions(sta);
}
}
rcu_read_unlock();
/* add back keys */
list_for_each_entry(sdata, &local->interfaces, list)
if (ieee80211_sdata_running(sdata))
@ -1219,10 +1225,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
add_timer(&local->sta_cleanup);
spin_lock_irqsave(&local->sta_lock, flags);
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list)
mesh_plink_restart(sta);
spin_unlock_irqrestore(&local->sta_lock, flags);
mutex_unlock(&local->sta_mtx);
#else
WARN_ON(1);
#endif

View file

@ -2,6 +2,16 @@
* Radiotap parser
*
* Copyright 2007 Andy Green <andy@warmcat.com>
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See COPYING for more details.
*/
#include <net/cfg80211.h>
@ -10,6 +20,35 @@
/* function prototypes and related defs are in include/net/cfg80211.h */
static const struct radiotap_align_size rtap_namespace_sizes[] = {
[IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, },
[IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, },
[IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, },
/*
* add more here as they are defined in radiotap.h
*/
};
static const struct ieee80211_radiotap_namespace radiotap_ns = {
.n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]),
.align_size = rtap_namespace_sizes,
};
/**
* ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
* @iterator: radiotap_iterator to initialize
@ -50,9 +89,9 @@
*/
int ieee80211_radiotap_iterator_init(
struct ieee80211_radiotap_iterator *iterator,
struct ieee80211_radiotap_header *radiotap_header,
int max_length)
struct ieee80211_radiotap_iterator *iterator,
struct ieee80211_radiotap_header *radiotap_header,
int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
{
/* Linux only supports version 0 radiotap format */
if (radiotap_header->it_version)
@ -62,19 +101,24 @@ int ieee80211_radiotap_iterator_init(
if (max_length < get_unaligned_le16(&radiotap_header->it_len))
return -EINVAL;
iterator->rtheader = radiotap_header;
iterator->max_length = get_unaligned_le16(&radiotap_header->it_len);
iterator->arg_index = 0;
iterator->bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header);
iterator->this_arg = NULL;
iterator->_rtheader = radiotap_header;
iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len);
iterator->_arg_index = 0;
iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header);
iterator->_reset_on_ext = 0;
iterator->_next_bitmap = &radiotap_header->it_present;
iterator->_next_bitmap++;
iterator->_vns = vns;
iterator->current_namespace = &radiotap_ns;
iterator->is_radiotap_ns = 1;
/* find payload start allowing for extended bitmap(s) */
if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) {
while (get_unaligned_le32(iterator->arg) &
(1 << IEEE80211_RADIOTAP_EXT)) {
iterator->arg += sizeof(u32);
if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
while (get_unaligned_le32(iterator->_arg) &
(1 << IEEE80211_RADIOTAP_EXT)) {
iterator->_arg += sizeof(uint32_t);
/*
* check for insanity where the present bitmaps
@ -82,12 +126,13 @@ int ieee80211_radiotap_iterator_init(
* stated radiotap header length
*/
if (((ulong)iterator->arg -
(ulong)iterator->rtheader) > iterator->max_length)
if ((unsigned long)iterator->_arg -
(unsigned long)iterator->_rtheader >
(unsigned long)iterator->_max_length)
return -EINVAL;
}
iterator->arg += sizeof(u32);
iterator->_arg += sizeof(uint32_t);
/*
* no need to check again for blowing past stated radiotap
@ -96,12 +141,36 @@ int ieee80211_radiotap_iterator_init(
*/
}
iterator->this_arg = iterator->_arg;
/* we are all initialized happily */
return 0;
}
EXPORT_SYMBOL(ieee80211_radiotap_iterator_init);
static void find_ns(struct ieee80211_radiotap_iterator *iterator,
uint32_t oui, uint8_t subns)
{
int i;
iterator->current_namespace = NULL;
if (!iterator->_vns)
return;
for (i = 0; i < iterator->_vns->n_ns; i++) {
if (iterator->_vns->ns[i].oui != oui)
continue;
if (iterator->_vns->ns[i].subns != subns)
continue;
iterator->current_namespace = &iterator->_vns->ns[i];
break;
}
}
/**
* ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
@ -127,99 +196,80 @@ EXPORT_SYMBOL(ieee80211_radiotap_iterator_init);
*/
int ieee80211_radiotap_iterator_next(
struct ieee80211_radiotap_iterator *iterator)
struct ieee80211_radiotap_iterator *iterator)
{
/*
* small length lookup table for all radiotap types we heard of
* starting from b0 in the bitmap, so we can walk the payload
* area of the radiotap header
*
* There is a requirement to pad args, so that args
* of a given length must begin at a boundary of that length
* -- but note that compound args are allowed (eg, 2 x u16
* for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not
* a reliable indicator of alignment requirement.
*
* upper nybble: content alignment for arg
* lower nybble: content length for arg
*/
static const u8 rt_sizes[] = {
[IEEE80211_RADIOTAP_TSFT] = 0x88,
[IEEE80211_RADIOTAP_FLAGS] = 0x11,
[IEEE80211_RADIOTAP_RATE] = 0x11,
[IEEE80211_RADIOTAP_CHANNEL] = 0x24,
[IEEE80211_RADIOTAP_FHSS] = 0x22,
[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11,
[IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11,
[IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22,
[IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22,
[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22,
[IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11,
[IEEE80211_RADIOTAP_ANTENNA] = 0x11,
[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11,
[IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11,
[IEEE80211_RADIOTAP_RX_FLAGS] = 0x22,
[IEEE80211_RADIOTAP_TX_FLAGS] = 0x22,
[IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11,
[IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11,
/*
* add more here as they are defined in
* include/net/ieee80211_radiotap.h
*/
};
/*
* for every radiotap entry we can at
* least skip (by knowing the length)...
*/
while (iterator->arg_index < sizeof(rt_sizes)) {
while (1) {
int hit = 0;
int pad;
int pad, align, size, subns, vnslen;
uint32_t oui;
if (!(iterator->bitmap_shifter & 1))
/* if no more EXT bits, that's it */
if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT &&
!(iterator->_bitmap_shifter & 1))
return -ENOENT;
if (!(iterator->_bitmap_shifter & 1))
goto next_entry; /* arg not present */
/* get alignment/size of data */
switch (iterator->_arg_index % 32) {
case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
case IEEE80211_RADIOTAP_EXT:
align = 1;
size = 0;
break;
case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
align = 2;
size = 6;
break;
default:
if (!iterator->current_namespace ||
iterator->_arg_index >= iterator->current_namespace->n_bits) {
if (iterator->current_namespace == &radiotap_ns)
return -ENOENT;
align = 0;
} else {
align = iterator->current_namespace->align_size[iterator->_arg_index].align;
size = iterator->current_namespace->align_size[iterator->_arg_index].size;
}
if (!align) {
/* skip all subsequent data */
iterator->_arg = iterator->_next_ns_data;
/* give up on this namespace */
iterator->current_namespace = NULL;
goto next_entry;
}
break;
}
/*
* arg is present, account for alignment padding
* 8-bit args can be at any alignment
* 16-bit args must start on 16-bit boundary
* 32-bit args must start on 32-bit boundary
* 64-bit args must start on 64-bit boundary
*
* note that total arg size can differ from alignment of
* elements inside arg, so we use upper nybble of length
* table to base alignment on
*
* also note: these alignments are ** relative to the
* start of the radiotap header **. There is no guarantee
* Note that these alignments are relative to the start
* of the radiotap header. There is no guarantee
* that the radiotap header itself is aligned on any
* kind of boundary.
*
* the above is why get_unaligned() is used to dereference
* multibyte elements from the radiotap area
* The above is why get_unaligned() is used to dereference
* multibyte elements from the radiotap area.
*/
pad = (((ulong)iterator->arg) -
((ulong)iterator->rtheader)) &
((rt_sizes[iterator->arg_index] >> 4) - 1);
pad = ((unsigned long)iterator->_arg -
(unsigned long)iterator->_rtheader) & (align - 1);
if (pad)
iterator->arg +=
(rt_sizes[iterator->arg_index] >> 4) - pad;
iterator->_arg += align - pad;
/*
* this is what we will return to user, but we need to
* move on first so next call has something fresh to test
*/
iterator->this_arg_index = iterator->arg_index;
iterator->this_arg = iterator->arg;
hit = 1;
iterator->this_arg_index = iterator->_arg_index;
iterator->this_arg = iterator->_arg;
iterator->this_arg_size = size;
/* internally move on the size of this arg */
iterator->arg += rt_sizes[iterator->arg_index] & 0x0f;
iterator->_arg += size;
/*
* check for insanity where we are given a bitmap that
@ -228,32 +278,73 @@ int ieee80211_radiotap_iterator_next(
* max_length on the last arg, never exceeding it.
*/
if (((ulong)iterator->arg - (ulong)iterator->rtheader) >
iterator->max_length)
if ((unsigned long)iterator->_arg -
(unsigned long)iterator->_rtheader >
(unsigned long)iterator->_max_length)
return -EINVAL;
next_entry:
iterator->arg_index++;
if (unlikely((iterator->arg_index & 31) == 0)) {
/* completed current u32 bitmap */
if (iterator->bitmap_shifter & 1) {
/* b31 was set, there is more */
/* move to next u32 bitmap */
iterator->bitmap_shifter =
get_unaligned_le32(iterator->next_bitmap);
iterator->next_bitmap++;
} else
/* no more bitmaps: end */
iterator->arg_index = sizeof(rt_sizes);
} else /* just try the next bit */
iterator->bitmap_shifter >>= 1;
/* these special ones are valid in each bitmap word */
switch (iterator->_arg_index % 32) {
case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
iterator->_bitmap_shifter >>= 1;
iterator->_arg_index++;
iterator->_reset_on_ext = 1;
vnslen = get_unaligned_le16(iterator->this_arg + 4);
iterator->_next_ns_data = iterator->_arg + vnslen;
oui = (*iterator->this_arg << 16) |
(*(iterator->this_arg + 1) << 8) |
*(iterator->this_arg + 2);
subns = *(iterator->this_arg + 3);
find_ns(iterator, oui, subns);
iterator->is_radiotap_ns = 0;
/* allow parsers to show this information */
iterator->this_arg_index =
IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
iterator->this_arg_size += vnslen;
if ((unsigned long)iterator->this_arg +
iterator->this_arg_size -
(unsigned long)iterator->_rtheader >
(unsigned long)(unsigned long)iterator->_max_length)
return -EINVAL;
hit = 1;
break;
case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
iterator->_bitmap_shifter >>= 1;
iterator->_arg_index++;
iterator->_reset_on_ext = 1;
iterator->current_namespace = &radiotap_ns;
iterator->is_radiotap_ns = 1;
break;
case IEEE80211_RADIOTAP_EXT:
/*
* bit 31 was set, there is more
* -- move to next u32 bitmap
*/
iterator->_bitmap_shifter =
get_unaligned_le32(iterator->_next_bitmap);
iterator->_next_bitmap++;
if (iterator->_reset_on_ext)
iterator->_arg_index = 0;
else
iterator->_arg_index++;
iterator->_reset_on_ext = 0;
break;
default:
/* we've got a hit! */
hit = 1;
next_entry:
iterator->_bitmap_shifter >>= 1;
iterator->_arg_index++;
}
/* if we found a valid arg earlier, return it now */
if (hit)
return 0;
}
/* we don't know how to handle any more args, we're done */
return -ENOENT;
}
EXPORT_SYMBOL(ieee80211_radiotap_iterator_next);