diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-25 13:25:22 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-25 13:25:22 +0200 |
commit | 8a9ea3237e7eb5c25f09e429ad242ae5a3d5ea22 (patch) | |
tree | a0a63398a9983667d52cbbbf4e2405b4f22b1d83 /drivers/net/wireless/p54/main.c | |
parent | Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/greg... (diff) | |
parent | dp83640: free packet queues on remove (diff) | |
download | linux-8a9ea3237e7eb5c25f09e429ad242ae5a3d5ea22.tar.xz linux-8a9ea3237e7eb5c25f09e429ad242ae5a3d5ea22.zip |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1745 commits)
dp83640: free packet queues on remove
dp83640: use proper function to free transmit time stamping packets
ipv6: Do not use routes from locally generated RAs
|PATCH net-next] tg3: add tx_dropped counter
be2net: don't create multiple RX/TX rings in multi channel mode
be2net: don't create multiple TXQs in BE2
be2net: refactor VF setup/teardown code into be_vf_setup/clear()
be2net: add vlan/rx-mode/flow-control config to be_setup()
net_sched: cls_flow: use skb_header_pointer()
ipv4: avoid useless call of the function check_peer_pmtu
TCP: remove TCP_DEBUG
net: Fix driver name for mdio-gpio.c
ipv4: tcp: fix TOS value in ACK messages sent from TIME_WAIT
rtnetlink: Add missing manual netlink notification in dev_change_net_namespaces
ipv4: fix ipsec forward performance regression
jme: fix irq storm after suspend/resume
route: fix ICMP redirect validation
net: hold sock reference while processing tx timestamps
tcp: md5: add more const attributes
Add ethtool -g support to virtio_net
...
Fix up conflicts in:
- drivers/net/Kconfig:
The split-up generated a trivial conflict with removal of a
stale reference to Documentation/networking/net-modules.txt.
Remove it from the new location instead.
- fs/sysfs/dir.c:
Fairly nasty conflicts with the sysfs rb-tree usage, conflicting
with Eric Biederman's changes for tagged directories.
Diffstat (limited to 'drivers/net/wireless/p54/main.c')
-rw-r--r-- | drivers/net/wireless/p54/main.c | 116 |
1 files changed, 103 insertions, 13 deletions
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index a5a6d9e647bb..ad9ae04d07aa 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -204,13 +204,11 @@ static void p54_stop(struct ieee80211_hw *dev) struct p54_common *priv = dev->priv; int i; - mutex_lock(&priv->conf_mutex); priv->mode = NL80211_IFTYPE_UNSPECIFIED; priv->softled_state = 0; - p54_set_leds(priv); - cancel_delayed_work_sync(&priv->work); - + mutex_lock(&priv->conf_mutex); + p54_set_leds(priv); priv->stop(dev); skb_queue_purge(&priv->tx_pending); skb_queue_purge(&priv->tx_queue); @@ -278,6 +276,42 @@ static void p54_remove_interface(struct ieee80211_hw *dev, mutex_unlock(&priv->conf_mutex); } +static int p54_wait_for_stats(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + int ret; + + priv->update_stats = true; + ret = p54_fetch_statistics(priv); + if (ret) + return ret; + + ret = wait_for_completion_interruptible_timeout(&priv->stat_comp, HZ); + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + +static void p54_reset_stats(struct p54_common *priv) +{ + struct ieee80211_channel *chan = priv->curchan; + + if (chan) { + struct survey_info *info = &priv->survey[chan->hw_value]; + + /* only reset channel statistics, don't touch .filled, etc. */ + info->channel_time = 0; + info->channel_time_busy = 0; + info->channel_time_tx = 0; + } + + priv->update_stats = true; + priv->survey_raw.active = 0; + priv->survey_raw.cca = 0; + priv->survey_raw.tx = 0; +} + static int p54_config(struct ieee80211_hw *dev, u32 changed) { int ret = 0; @@ -288,19 +322,36 @@ static int p54_config(struct ieee80211_hw *dev, u32 changed) if (changed & IEEE80211_CONF_CHANGE_POWER) priv->output_power = conf->power_level << 2; if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + struct ieee80211_channel *oldchan; + WARN_ON(p54_wait_for_stats(dev)); + oldchan = priv->curchan; + priv->curchan = NULL; ret = p54_scan(priv, P54_SCAN_EXIT, 0); - if (ret) + if (ret) { + priv->curchan = oldchan; goto out; + } + /* + * TODO: Use the LM_SCAN_TRAP to determine the current + * operating channel. + */ + priv->curchan = priv->hw->conf.channel; + p54_reset_stats(priv); + WARN_ON(p54_fetch_statistics(priv)); } if (changed & IEEE80211_CONF_CHANGE_PS) { + WARN_ON(p54_wait_for_stats(dev)); ret = p54_set_ps(priv); if (ret) goto out; + WARN_ON(p54_wait_for_stats(dev)); } if (changed & IEEE80211_CONF_CHANGE_IDLE) { + WARN_ON(p54_wait_for_stats(dev)); ret = p54_setup_mac(priv); if (ret) goto out; + WARN_ON(p54_wait_for_stats(dev)); } out: @@ -353,7 +404,8 @@ static void p54_configure_filter(struct ieee80211_hw *dev, p54_set_groupfilter(priv); } -static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue, +static int p54_conf_tx(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct p54_common *priv = dev->priv; @@ -384,7 +436,9 @@ static void p54_work(struct work_struct *work) * 2. cancel stuck frames / reset the device if necessary. */ - p54_fetch_statistics(priv); + mutex_lock(&priv->conf_mutex); + WARN_ON_ONCE(p54_fetch_statistics(priv)); + mutex_unlock(&priv->conf_mutex); } static int p54_get_stats(struct ieee80211_hw *dev, @@ -541,16 +595,47 @@ static int p54_get_survey(struct ieee80211_hw *dev, int idx, struct survey_info *survey) { struct p54_common *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; + struct ieee80211_channel *chan; + int err, tries; + bool in_use = false; - if (idx != 0) + if (idx >= priv->chan_num) return -ENOENT; - survey->channel = conf->channel; - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = clamp_t(s8, priv->noise, -128, 127); +#define MAX_TRIES 1 + for (tries = 0; tries < MAX_TRIES; tries++) { + chan = priv->curchan; + if (chan && chan->hw_value == idx) { + mutex_lock(&priv->conf_mutex); + err = p54_wait_for_stats(dev); + mutex_unlock(&priv->conf_mutex); + if (err) + return err; + + in_use = true; + } - return 0; + memcpy(survey, &priv->survey[idx], sizeof(*survey)); + + if (in_use) { + /* test if the reported statistics are valid. */ + if (survey->channel_time != 0) { + survey->filled |= SURVEY_INFO_IN_USE; + } else { + /* + * hw/fw has not accumulated enough sample sets. + * Wait for 100ms, this ought to be enough to + * to get at least one non-null set of channel + * usage statistics. + */ + msleep(100); + continue; + } + } + return 0; + } + return -ETIMEDOUT; +#undef MAX_TRIES } static unsigned int p54_flush_count(struct p54_common *priv) @@ -686,11 +771,14 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) mutex_init(&priv->conf_mutex); mutex_init(&priv->eeprom_mutex); + init_completion(&priv->stat_comp); init_completion(&priv->eeprom_comp); init_completion(&priv->beacon_comp); INIT_DELAYED_WORK(&priv->work, p54_work); memset(&priv->mc_maclist[0], ~0, ETH_ALEN); + priv->curchan = NULL; + p54_reset_stats(priv); return dev; } EXPORT_SYMBOL_GPL(p54_init_common); @@ -730,11 +818,13 @@ void p54_free_common(struct ieee80211_hw *dev) kfree(priv->curve_data); kfree(priv->rssi_db); kfree(priv->used_rxkeys); + kfree(priv->survey); priv->iq_autocal = NULL; priv->output_limit = NULL; priv->curve_data = NULL; priv->rssi_db = NULL; priv->used_rxkeys = NULL; + priv->survey = NULL; ieee80211_free_hw(dev); } EXPORT_SYMBOL_GPL(p54_free_common); |