linux/net/mac80211/offchannel.c
<<
>>
Prefs
   1/*
   2 * Off-channel operation helpers
   3 *
   4 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
   5 * Copyright 2004, Instant802 Networks, Inc.
   6 * Copyright 2005, Devicescape Software, Inc.
   7 * Copyright 2006-2007  Jiri Benc <jbenc@suse.cz>
   8 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
   9 * Copyright 2009       Johannes Berg <johannes@sipsolutions.net>
  10 *
  11 * This program is free software; you can redistribute it and/or modify
  12 * it under the terms of the GNU General Public License version 2 as
  13 * published by the Free Software Foundation.
  14 */
  15#include <linux/export.h>
  16#include <net/mac80211.h>
  17#include "ieee80211_i.h"
  18#include "driver-ops.h"
  19
  20/*
  21 * Tell our hardware to disable PS.
  22 * Optionally inform AP that we will go to sleep so that it will buffer
  23 * the frames while we are doing off-channel work.  This is optional
  24 * because we *may* be doing work on-operating channel, and want our
  25 * hardware unconditionally awake, but still let the AP send us normal frames.
  26 */
  27static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
  28{
  29        struct ieee80211_local *local = sdata->local;
  30        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  31
  32        local->offchannel_ps_enabled = false;
  33
  34        /* FIXME: what to do when local->pspolling is true? */
  35
  36        del_timer_sync(&local->dynamic_ps_timer);
  37        del_timer_sync(&ifmgd->bcn_mon_timer);
  38        del_timer_sync(&ifmgd->conn_mon_timer);
  39
  40        cancel_work_sync(&local->dynamic_ps_enable_work);
  41
  42        if (local->hw.conf.flags & IEEE80211_CONF_PS) {
  43                local->offchannel_ps_enabled = true;
  44                local->hw.conf.flags &= ~IEEE80211_CONF_PS;
  45                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
  46        }
  47
  48        if (!local->offchannel_ps_enabled ||
  49            !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
  50                /*
  51                 * If power save was enabled, no need to send a nullfunc
  52                 * frame because AP knows that we are sleeping. But if the
  53                 * hardware is creating the nullfunc frame for power save
  54                 * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not
  55                 * enabled) and power save was enabled, the firmware just
  56                 * sent a null frame with power save disabled. So we need
  57                 * to send a new nullfunc frame to inform the AP that we
  58                 * are again sleeping.
  59                 */
  60                ieee80211_send_nullfunc(local, sdata, 1);
  61}
  62
  63/* inform AP that we are awake again, unless power save is enabled */
  64static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
  65{
  66        struct ieee80211_local *local = sdata->local;
  67
  68        if (!local->ps_sdata)
  69                ieee80211_send_nullfunc(local, sdata, 0);
  70        else if (local->offchannel_ps_enabled) {
  71                /*
  72                 * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
  73                 * will send a nullfunc frame with the powersave bit set
  74                 * even though the AP already knows that we are sleeping.
  75                 * This could be avoided by sending a null frame with power
  76                 * save bit disabled before enabling the power save, but
  77                 * this doesn't gain anything.
  78                 *
  79                 * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need
  80                 * to send a nullfunc frame because AP already knows that
  81                 * we are sleeping, let's just enable power save mode in
  82                 * hardware.
  83                 */
  84                /* TODO:  Only set hardware if CONF_PS changed?
  85                 * TODO:  Should we set offchannel_ps_enabled to false?
  86                 */
  87                local->hw.conf.flags |= IEEE80211_CONF_PS;
  88                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
  89        } else if (local->hw.conf.dynamic_ps_timeout > 0) {
  90                /*
  91                 * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer
  92                 * had been running before leaving the operating channel,
  93                 * restart the timer now and send a nullfunc frame to inform
  94                 * the AP that we are awake.
  95                 */
  96                ieee80211_send_nullfunc(local, sdata, 0);
  97                mod_timer(&local->dynamic_ps_timer, jiffies +
  98                          msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
  99        }
 100
 101        ieee80211_sta_reset_beacon_monitor(sdata);
 102        ieee80211_sta_reset_conn_monitor(sdata);
 103}
 104
 105void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
 106                                    bool offchannel_ps_enable)
 107{
 108        struct ieee80211_sub_if_data *sdata;
 109
 110        /*
 111         * notify the AP about us leaving the channel and stop all
 112         * STA interfaces.
 113         */
 114        mutex_lock(&local->iflist_mtx);
 115        list_for_each_entry(sdata, &local->interfaces, list) {
 116                if (!ieee80211_sdata_running(sdata))
 117                        continue;
 118
 119                if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE)
 120                        continue;
 121
 122                if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
 123                        set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
 124
 125                /* Check to see if we should disable beaconing. */
 126                if (sdata->vif.type == NL80211_IFTYPE_AP ||
 127                    sdata->vif.type == NL80211_IFTYPE_ADHOC ||
 128                    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
 129                        ieee80211_bss_info_change_notify(
 130                                sdata, BSS_CHANGED_BEACON_ENABLED);
 131
 132                if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
 133                        netif_tx_stop_all_queues(sdata->dev);
 134                        if (offchannel_ps_enable &&
 135                            (sdata->vif.type == NL80211_IFTYPE_STATION) &&
 136                            sdata->u.mgd.associated)
 137                                ieee80211_offchannel_ps_enable(sdata);
 138                }
 139        }
 140        mutex_unlock(&local->iflist_mtx);
 141}
 142
 143void ieee80211_offchannel_return(struct ieee80211_local *local,
 144                                 bool offchannel_ps_disable)
 145{
 146        struct ieee80211_sub_if_data *sdata;
 147
 148        mutex_lock(&local->iflist_mtx);
 149        list_for_each_entry(sdata, &local->interfaces, list) {
 150                if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE)
 151                        continue;
 152
 153                if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
 154                        clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
 155
 156                if (!ieee80211_sdata_running(sdata))
 157                        continue;
 158
 159                /* Tell AP we're back */
 160                if (offchannel_ps_disable &&
 161                    sdata->vif.type == NL80211_IFTYPE_STATION) {
 162                        if (sdata->u.mgd.associated)
 163                                ieee80211_offchannel_ps_disable(sdata);
 164                }
 165
 166                if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
 167                        /*
 168                         * This may wake up queues even though the driver
 169                         * currently has them stopped. This is not very
 170                         * likely, since the driver won't have gotten any
 171                         * (or hardly any) new packets while we weren't
 172                         * on the right channel, and even if it happens
 173                         * it will at most lead to queueing up one more
 174                         * packet per queue in mac80211 rather than on
 175                         * the interface qdisc.
 176                         */
 177                        netif_tx_wake_all_queues(sdata->dev);
 178                }
 179
 180                if (sdata->vif.type == NL80211_IFTYPE_AP ||
 181                    sdata->vif.type == NL80211_IFTYPE_ADHOC ||
 182                    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
 183                        ieee80211_bss_info_change_notify(
 184                                sdata, BSS_CHANGED_BEACON_ENABLED);
 185        }
 186        mutex_unlock(&local->iflist_mtx);
 187}
 188
 189void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
 190{
 191        if (roc->notified)
 192                return;
 193
 194        if (roc->mgmt_tx_cookie) {
 195                if (!WARN_ON(!roc->frame)) {
 196                        ieee80211_tx_skb(roc->sdata, roc->frame);
 197                        roc->frame = NULL;
 198                }
 199        } else {
 200                cfg80211_ready_on_channel(&roc->sdata->wdev, (unsigned long)roc,
 201                                          roc->chan, roc->chan_type,
 202                                          roc->req_duration, GFP_KERNEL);
 203        }
 204
 205        roc->notified = true;
 206}
 207
 208static void ieee80211_hw_roc_start(struct work_struct *work)
 209{
 210        struct ieee80211_local *local =
 211                container_of(work, struct ieee80211_local, hw_roc_start);
 212        struct ieee80211_roc_work *roc, *dep, *tmp;
 213
 214        mutex_lock(&local->mtx);
 215
 216        if (list_empty(&local->roc_list))
 217                goto out_unlock;
 218
 219        roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
 220                               list);
 221
 222        if (!roc->started)
 223                goto out_unlock;
 224
 225        roc->hw_begun = true;
 226        roc->hw_start_time = local->hw_roc_start_time;
 227
 228        ieee80211_handle_roc_started(roc);
 229        list_for_each_entry_safe(dep, tmp, &roc->dependents, list) {
 230                ieee80211_handle_roc_started(dep);
 231
 232                if (dep->duration > roc->duration) {
 233                        u32 dur = dep->duration;
 234                        dep->duration = dur - roc->duration;
 235                        roc->duration = dur;
 236                        list_move(&dep->list, &roc->list);
 237                }
 238        }
 239 out_unlock:
 240        mutex_unlock(&local->mtx);
 241}
 242
 243void ieee80211_ready_on_channel(struct ieee80211_hw *hw)
 244{
 245        struct ieee80211_local *local = hw_to_local(hw);
 246
 247        local->hw_roc_start_time = jiffies;
 248
 249        trace_api_ready_on_channel(local);
 250
 251        ieee80211_queue_work(hw, &local->hw_roc_start);
 252}
 253EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
 254
 255void ieee80211_start_next_roc(struct ieee80211_local *local)
 256{
 257        struct ieee80211_roc_work *roc;
 258
 259        lockdep_assert_held(&local->mtx);
 260
 261        if (list_empty(&local->roc_list)) {
 262                ieee80211_run_deferred_scan(local);
 263                return;
 264        }
 265
 266        roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
 267                               list);
 268
 269        if (WARN_ON_ONCE(roc->started))
 270                return;
 271
 272        if (local->ops->remain_on_channel) {
 273                int ret, duration = roc->duration;
 274
 275                /* XXX: duplicated, see ieee80211_start_roc_work() */
 276                if (!duration)
 277                        duration = 10;
 278
 279                ret = drv_remain_on_channel(local, roc->chan,
 280                                            roc->chan_type,
 281                                            duration);
 282
 283                roc->started = true;
 284
 285                if (ret) {
 286                        wiphy_warn(local->hw.wiphy,
 287                                   "failed to start next HW ROC (%d)\n", ret);
 288                        /*
 289                         * queue the work struct again to avoid recursion
 290                         * when multiple failures occur
 291                         */
 292                        ieee80211_remain_on_channel_expired(&local->hw);
 293                }
 294        } else {
 295                /* delay it a bit */
 296                ieee80211_queue_delayed_work(&local->hw, &roc->work,
 297                                             round_jiffies_relative(HZ/2));
 298        }
 299}
 300
 301void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
 302{
 303        struct ieee80211_roc_work *dep, *tmp;
 304
 305        /* was never transmitted */
 306        if (roc->frame) {
 307                cfg80211_mgmt_tx_status(&roc->sdata->wdev,
 308                                        (unsigned long)roc->frame,
 309                                        roc->frame->data, roc->frame->len,
 310                                        false, GFP_KERNEL);
 311                kfree_skb(roc->frame);
 312        }
 313
 314        if (!roc->mgmt_tx_cookie)
 315                cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
 316                                                   (unsigned long)roc,
 317                                                   roc->chan, roc->chan_type,
 318                                                   GFP_KERNEL);
 319
 320        list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
 321                ieee80211_roc_notify_destroy(dep);
 322
 323        kfree(roc);
 324}
 325
 326void ieee80211_sw_roc_work(struct work_struct *work)
 327{
 328        struct ieee80211_roc_work *roc =
 329                container_of(work, struct ieee80211_roc_work, work.work);
 330        struct ieee80211_sub_if_data *sdata = roc->sdata;
 331        struct ieee80211_local *local = sdata->local;
 332        bool started;
 333
 334        mutex_lock(&local->mtx);
 335
 336        if (roc->abort)
 337                goto finish;
 338
 339        if (WARN_ON(list_empty(&local->roc_list)))
 340                goto out_unlock;
 341
 342        if (WARN_ON(roc != list_first_entry(&local->roc_list,
 343                                            struct ieee80211_roc_work,
 344                                            list)))
 345                goto out_unlock;
 346
 347        if (!roc->started) {
 348                struct ieee80211_roc_work *dep;
 349
 350                /* start this ROC */
 351
 352                /* switch channel etc */
 353                ieee80211_recalc_idle(local);
 354
 355                local->tmp_channel = roc->chan;
 356                local->tmp_channel_type = roc->chan_type;
 357                ieee80211_hw_config(local, 0);
 358
 359                /* tell userspace or send frame */
 360                ieee80211_handle_roc_started(roc);
 361                list_for_each_entry(dep, &roc->dependents, list)
 362                        ieee80211_handle_roc_started(dep);
 363
 364                /* if it was pure TX, just finish right away */
 365                if (!roc->duration)
 366                        goto finish;
 367
 368                roc->started = true;
 369                ieee80211_queue_delayed_work(&local->hw, &roc->work,
 370                                             msecs_to_jiffies(roc->duration));
 371        } else {
 372                /* finish this ROC */
 373 finish:
 374                list_del(&roc->list);
 375                started = roc->started;
 376                ieee80211_roc_notify_destroy(roc);
 377
 378                if (started) {
 379                        drv_flush(local, false);
 380
 381                        local->tmp_channel = NULL;
 382                        ieee80211_hw_config(local, 0);
 383
 384                        ieee80211_offchannel_return(local, true);
 385                }
 386
 387                ieee80211_recalc_idle(local);
 388
 389                if (started)
 390                        ieee80211_start_next_roc(local);
 391        }
 392
 393 out_unlock:
 394        mutex_unlock(&local->mtx);
 395}
 396
 397static void ieee80211_hw_roc_done(struct work_struct *work)
 398{
 399        struct ieee80211_local *local =
 400                container_of(work, struct ieee80211_local, hw_roc_done);
 401        struct ieee80211_roc_work *roc;
 402
 403        mutex_lock(&local->mtx);
 404
 405        if (list_empty(&local->roc_list))
 406                goto out_unlock;
 407
 408        roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
 409                               list);
 410
 411        if (!roc->started)
 412                goto out_unlock;
 413
 414        list_del(&roc->list);
 415
 416        ieee80211_roc_notify_destroy(roc);
 417
 418        /* if there's another roc, start it now */
 419        ieee80211_start_next_roc(local);
 420
 421 out_unlock:
 422        mutex_unlock(&local->mtx);
 423}
 424
 425void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw)
 426{
 427        struct ieee80211_local *local = hw_to_local(hw);
 428
 429        trace_api_remain_on_channel_expired(local);
 430
 431        ieee80211_queue_work(hw, &local->hw_roc_done);
 432}
 433EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
 434
 435void ieee80211_roc_setup(struct ieee80211_local *local)
 436{
 437        INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
 438        INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
 439        INIT_LIST_HEAD(&local->roc_list);
 440}
 441
 442void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata)
 443{
 444        struct ieee80211_local *local = sdata->local;
 445        struct ieee80211_roc_work *roc, *tmp;
 446        LIST_HEAD(tmp_list);
 447
 448        mutex_lock(&local->mtx);
 449        list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
 450                if (roc->sdata != sdata)
 451                        continue;
 452
 453                if (roc->started && local->ops->remain_on_channel) {
 454                        /* can race, so ignore return value */
 455                        drv_cancel_remain_on_channel(local);
 456                }
 457
 458                list_move_tail(&roc->list, &tmp_list);
 459                roc->abort = true;
 460        }
 461        mutex_unlock(&local->mtx);
 462
 463        list_for_each_entry_safe(roc, tmp, &tmp_list, list) {
 464                if (local->ops->remain_on_channel) {
 465                        list_del(&roc->list);
 466                        ieee80211_roc_notify_destroy(roc);
 467                } else {
 468                        ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
 469
 470                        /* work will clean up etc */
 471                        flush_delayed_work(&roc->work);
 472                }
 473        }
 474
 475        WARN_ON_ONCE(!list_empty(&tmp_list));
 476}
 477
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.