diff options
Diffstat (limited to 'drivers/net/wireless/p54/main.c')
| -rw-r--r-- | drivers/net/wireless/p54/main.c | 117 |
1 files changed, 104 insertions, 13 deletions
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index a5a6d9e647bb..db4d9a02f264 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
| 21 | #include <linux/firmware.h> | 21 | #include <linux/firmware.h> |
| 22 | #include <linux/etherdevice.h> | 22 | #include <linux/etherdevice.h> |
| 23 | #include <linux/module.h> | ||
| 23 | 24 | ||
| 24 | #include <net/mac80211.h> | 25 | #include <net/mac80211.h> |
| 25 | 26 | ||
| @@ -204,13 +205,11 @@ static void p54_stop(struct ieee80211_hw *dev) | |||
| 204 | struct p54_common *priv = dev->priv; | 205 | struct p54_common *priv = dev->priv; |
| 205 | int i; | 206 | int i; |
| 206 | 207 | ||
| 207 | mutex_lock(&priv->conf_mutex); | ||
| 208 | priv->mode = NL80211_IFTYPE_UNSPECIFIED; | 208 | priv->mode = NL80211_IFTYPE_UNSPECIFIED; |
| 209 | priv->softled_state = 0; | 209 | priv->softled_state = 0; |
| 210 | p54_set_leds(priv); | ||
| 211 | |||
| 212 | cancel_delayed_work_sync(&priv->work); | 210 | cancel_delayed_work_sync(&priv->work); |
| 213 | 211 | mutex_lock(&priv->conf_mutex); | |
| 212 | p54_set_leds(priv); | ||
| 214 | priv->stop(dev); | 213 | priv->stop(dev); |
| 215 | skb_queue_purge(&priv->tx_pending); | 214 | skb_queue_purge(&priv->tx_pending); |
| 216 | skb_queue_purge(&priv->tx_queue); | 215 | skb_queue_purge(&priv->tx_queue); |
| @@ -278,6 +277,42 @@ static void p54_remove_interface(struct ieee80211_hw *dev, | |||
| 278 | mutex_unlock(&priv->conf_mutex); | 277 | mutex_unlock(&priv->conf_mutex); |
| 279 | } | 278 | } |
| 280 | 279 | ||
| 280 | static int p54_wait_for_stats(struct ieee80211_hw *dev) | ||
| 281 | { | ||
| 282 | struct p54_common *priv = dev->priv; | ||
| 283 | int ret; | ||
| 284 | |||
| 285 | priv->update_stats = true; | ||
| 286 | ret = p54_fetch_statistics(priv); | ||
| 287 | if (ret) | ||
| 288 | return ret; | ||
| 289 | |||
| 290 | ret = wait_for_completion_interruptible_timeout(&priv->stat_comp, HZ); | ||
| 291 | if (ret == 0) | ||
| 292 | return -ETIMEDOUT; | ||
| 293 | |||
| 294 | return 0; | ||
| 295 | } | ||
| 296 | |||
| 297 | static void p54_reset_stats(struct p54_common *priv) | ||
| 298 | { | ||
| 299 | struct ieee80211_channel *chan = priv->curchan; | ||
| 300 | |||
| 301 | if (chan) { | ||
| 302 | struct survey_info *info = &priv->survey[chan->hw_value]; | ||
| 303 | |||
| 304 | /* only reset channel statistics, don't touch .filled, etc. */ | ||
| 305 | info->channel_time = 0; | ||
| 306 | info->channel_time_busy = 0; | ||
| 307 | info->channel_time_tx = 0; | ||
| 308 | } | ||
| 309 | |||
| 310 | priv->update_stats = true; | ||
| 311 | priv->survey_raw.active = 0; | ||
| 312 | priv->survey_raw.cca = 0; | ||
| 313 | priv->survey_raw.tx = 0; | ||
| 314 | } | ||
| 315 | |||
| 281 | static int p54_config(struct ieee80211_hw *dev, u32 changed) | 316 | static int p54_config(struct ieee80211_hw *dev, u32 changed) |
| 282 | { | 317 | { |
| 283 | int ret = 0; | 318 | int ret = 0; |
| @@ -288,19 +323,36 @@ static int p54_config(struct ieee80211_hw *dev, u32 changed) | |||
| 288 | if (changed & IEEE80211_CONF_CHANGE_POWER) | 323 | if (changed & IEEE80211_CONF_CHANGE_POWER) |
| 289 | priv->output_power = conf->power_level << 2; | 324 | priv->output_power = conf->power_level << 2; |
| 290 | if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { | 325 | if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { |
| 326 | struct ieee80211_channel *oldchan; | ||
| 327 | WARN_ON(p54_wait_for_stats(dev)); | ||
| 328 | oldchan = priv->curchan; | ||
| 329 | priv->curchan = NULL; | ||
| 291 | ret = p54_scan(priv, P54_SCAN_EXIT, 0); | 330 | ret = p54_scan(priv, P54_SCAN_EXIT, 0); |
| 292 | if (ret) | 331 | if (ret) { |
| 332 | priv->curchan = oldchan; | ||
| 293 | goto out; | 333 | goto out; |
| 334 | } | ||
| 335 | /* | ||
| 336 | * TODO: Use the LM_SCAN_TRAP to determine the current | ||
| 337 | * operating channel. | ||
| 338 | */ | ||
| 339 | priv->curchan = priv->hw->conf.channel; | ||
| 340 | p54_reset_stats(priv); | ||
| 341 | WARN_ON(p54_fetch_statistics(priv)); | ||
| 294 | } | 342 | } |
| 295 | if (changed & IEEE80211_CONF_CHANGE_PS) { | 343 | if (changed & IEEE80211_CONF_CHANGE_PS) { |
| 344 | WARN_ON(p54_wait_for_stats(dev)); | ||
| 296 | ret = p54_set_ps(priv); | 345 | ret = p54_set_ps(priv); |
| 297 | if (ret) | 346 | if (ret) |
| 298 | goto out; | 347 | goto out; |
| 348 | WARN_ON(p54_wait_for_stats(dev)); | ||
| 299 | } | 349 | } |
| 300 | if (changed & IEEE80211_CONF_CHANGE_IDLE) { | 350 | if (changed & IEEE80211_CONF_CHANGE_IDLE) { |
| 351 | WARN_ON(p54_wait_for_stats(dev)); | ||
| 301 | ret = p54_setup_mac(priv); | 352 | ret = p54_setup_mac(priv); |
| 302 | if (ret) | 353 | if (ret) |
| 303 | goto out; | 354 | goto out; |
| 355 | WARN_ON(p54_wait_for_stats(dev)); | ||
| 304 | } | 356 | } |
| 305 | 357 | ||
| 306 | out: | 358 | out: |
| @@ -353,7 +405,8 @@ static void p54_configure_filter(struct ieee80211_hw *dev, | |||
| 353 | p54_set_groupfilter(priv); | 405 | p54_set_groupfilter(priv); |
| 354 | } | 406 | } |
| 355 | 407 | ||
| 356 | static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue, | 408 | static int p54_conf_tx(struct ieee80211_hw *dev, |
| 409 | struct ieee80211_vif *vif, u16 queue, | ||
| 357 | const struct ieee80211_tx_queue_params *params) | 410 | const struct ieee80211_tx_queue_params *params) |
| 358 | { | 411 | { |
| 359 | struct p54_common *priv = dev->priv; | 412 | struct p54_common *priv = dev->priv; |
| @@ -384,7 +437,9 @@ static void p54_work(struct work_struct *work) | |||
| 384 | * 2. cancel stuck frames / reset the device if necessary. | 437 | * 2. cancel stuck frames / reset the device if necessary. |
| 385 | */ | 438 | */ |
| 386 | 439 | ||
| 387 | p54_fetch_statistics(priv); | 440 | mutex_lock(&priv->conf_mutex); |
| 441 | WARN_ON_ONCE(p54_fetch_statistics(priv)); | ||
| 442 | mutex_unlock(&priv->conf_mutex); | ||
| 388 | } | 443 | } |
| 389 | 444 | ||
| 390 | static int p54_get_stats(struct ieee80211_hw *dev, | 445 | static int p54_get_stats(struct ieee80211_hw *dev, |
| @@ -541,16 +596,47 @@ static int p54_get_survey(struct ieee80211_hw *dev, int idx, | |||
| 541 | struct survey_info *survey) | 596 | struct survey_info *survey) |
| 542 | { | 597 | { |
| 543 | struct p54_common *priv = dev->priv; | 598 | struct p54_common *priv = dev->priv; |
| 544 | struct ieee80211_conf *conf = &dev->conf; | 599 | struct ieee80211_channel *chan; |
| 600 | int err, tries; | ||
| 601 | bool in_use = false; | ||
| 545 | 602 | ||
| 546 | if (idx != 0) | 603 | if (idx >= priv->chan_num) |
| 547 | return -ENOENT; | 604 | return -ENOENT; |
| 548 | 605 | ||
| 549 | survey->channel = conf->channel; | 606 | #define MAX_TRIES 1 |
| 550 | survey->filled = SURVEY_INFO_NOISE_DBM; | 607 | for (tries = 0; tries < MAX_TRIES; tries++) { |
| 551 | survey->noise = clamp_t(s8, priv->noise, -128, 127); | 608 | chan = priv->curchan; |
| 609 | if (chan && chan->hw_value == idx) { | ||
| 610 | mutex_lock(&priv->conf_mutex); | ||
| 611 | err = p54_wait_for_stats(dev); | ||
| 612 | mutex_unlock(&priv->conf_mutex); | ||
| 613 | if (err) | ||
| 614 | return err; | ||
| 615 | |||
| 616 | in_use = true; | ||
| 617 | } | ||
| 552 | 618 | ||
| 553 | return 0; | 619 | memcpy(survey, &priv->survey[idx], sizeof(*survey)); |
| 620 | |||
| 621 | if (in_use) { | ||
| 622 | /* test if the reported statistics are valid. */ | ||
| 623 | if (survey->channel_time != 0) { | ||
| 624 | survey->filled |= SURVEY_INFO_IN_USE; | ||
| 625 | } else { | ||
| 626 | /* | ||
| 627 | * hw/fw has not accumulated enough sample sets. | ||
| 628 | * Wait for 100ms, this ought to be enough to | ||
| 629 | * to get at least one non-null set of channel | ||
| 630 | * usage statistics. | ||
| 631 | */ | ||
| 632 | msleep(100); | ||
| 633 | continue; | ||
| 634 | } | ||
| 635 | } | ||
| 636 | return 0; | ||
| 637 | } | ||
| 638 | return -ETIMEDOUT; | ||
| 639 | #undef MAX_TRIES | ||
| 554 | } | 640 | } |
| 555 | 641 | ||
| 556 | static unsigned int p54_flush_count(struct p54_common *priv) | 642 | static unsigned int p54_flush_count(struct p54_common *priv) |
| @@ -686,11 +772,14 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) | |||
| 686 | 772 | ||
| 687 | mutex_init(&priv->conf_mutex); | 773 | mutex_init(&priv->conf_mutex); |
| 688 | mutex_init(&priv->eeprom_mutex); | 774 | mutex_init(&priv->eeprom_mutex); |
| 775 | init_completion(&priv->stat_comp); | ||
| 689 | init_completion(&priv->eeprom_comp); | 776 | init_completion(&priv->eeprom_comp); |
| 690 | init_completion(&priv->beacon_comp); | 777 | init_completion(&priv->beacon_comp); |
| 691 | INIT_DELAYED_WORK(&priv->work, p54_work); | 778 | INIT_DELAYED_WORK(&priv->work, p54_work); |
| 692 | 779 | ||
| 693 | memset(&priv->mc_maclist[0], ~0, ETH_ALEN); | 780 | memset(&priv->mc_maclist[0], ~0, ETH_ALEN); |
| 781 | priv->curchan = NULL; | ||
| 782 | p54_reset_stats(priv); | ||
| 694 | return dev; | 783 | return dev; |
| 695 | } | 784 | } |
| 696 | EXPORT_SYMBOL_GPL(p54_init_common); | 785 | EXPORT_SYMBOL_GPL(p54_init_common); |
| @@ -730,11 +819,13 @@ void p54_free_common(struct ieee80211_hw *dev) | |||
| 730 | kfree(priv->curve_data); | 819 | kfree(priv->curve_data); |
| 731 | kfree(priv->rssi_db); | 820 | kfree(priv->rssi_db); |
| 732 | kfree(priv->used_rxkeys); | 821 | kfree(priv->used_rxkeys); |
| 822 | kfree(priv->survey); | ||
| 733 | priv->iq_autocal = NULL; | 823 | priv->iq_autocal = NULL; |
| 734 | priv->output_limit = NULL; | 824 | priv->output_limit = NULL; |
| 735 | priv->curve_data = NULL; | 825 | priv->curve_data = NULL; |
| 736 | priv->rssi_db = NULL; | 826 | priv->rssi_db = NULL; |
| 737 | priv->used_rxkeys = NULL; | 827 | priv->used_rxkeys = NULL; |
| 828 | priv->survey = NULL; | ||
| 738 | ieee80211_free_hw(dev); | 829 | ieee80211_free_hw(dev); |
| 739 | } | 830 | } |
| 740 | EXPORT_SYMBOL_GPL(p54_free_common); | 831 | EXPORT_SYMBOL_GPL(p54_free_common); |
