Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 1 | /* |
| 2 | * mac80211 work implementation |
| 3 | * |
| 4 | * Copyright 2003-2008, Jouni Malinen <j@w1.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 | |
| 16 | #include <linux/delay.h> |
| 17 | #include <linux/if_ether.h> |
| 18 | #include <linux/skbuff.h> |
| 19 | #include <linux/if_arp.h> |
| 20 | #include <linux/etherdevice.h> |
| 21 | #include <linux/crc32.h> |
Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 22 | #include <linux/slab.h> |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 23 | #include <net/mac80211.h> |
| 24 | #include <asm/unaligned.h> |
| 25 | |
| 26 | #include "ieee80211_i.h" |
| 27 | #include "rate.h" |
Johannes Berg | b2abb6e | 2011-07-19 10:39:53 +0200 | [diff] [blame] | 28 | #include "driver-ops.h" |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 29 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 30 | enum work_action { |
| 31 | WORK_ACT_NONE, |
| 32 | WORK_ACT_TIMEOUT, |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 33 | }; |
| 34 | |
| 35 | |
| 36 | /* utils */ |
| 37 | static inline void ASSERT_WORK_MTX(struct ieee80211_local *local) |
| 38 | { |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 39 | lockdep_assert_held(&local->mtx); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 40 | } |
| 41 | |
| 42 | /* |
| 43 | * We can have multiple work items (and connection probing) |
| 44 | * scheduling this timer, but we need to take care to only |
| 45 | * reschedule it when it should fire _earlier_ than it was |
| 46 | * asked for before, or if it's not pending right now. This |
| 47 | * function ensures that. Note that it then is required to |
| 48 | * run this function for all timeouts after the first one |
| 49 | * has happened -- the work that runs from this timer will |
| 50 | * do that. |
| 51 | */ |
| 52 | static void run_again(struct ieee80211_local *local, |
| 53 | unsigned long timeout) |
| 54 | { |
| 55 | ASSERT_WORK_MTX(local); |
| 56 | |
| 57 | if (!timer_pending(&local->work_timer) || |
| 58 | time_before(timeout, local->work_timer.expires)) |
| 59 | mod_timer(&local->work_timer, timeout); |
| 60 | } |
| 61 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 62 | void free_work(struct ieee80211_work *wk) |
| 63 | { |
Lai Jiangshan | a74ce14 | 2011-03-18 12:14:15 +0800 | [diff] [blame] | 64 | kfree_rcu(wk, rcu_head); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 65 | } |
| 66 | |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 67 | static enum work_action __must_check |
| 68 | ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk) |
| 69 | { |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 70 | /* |
| 71 | * First time we run, do nothing -- the generic code will |
| 72 | * have switched to the right channel etc. |
| 73 | */ |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 74 | if (!wk->started) { |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 75 | wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration); |
| 76 | |
Kalle Valo | 1990ca6 | 2009-12-30 14:42:20 +0200 | [diff] [blame] | 77 | cfg80211_ready_on_channel(wk->sdata->dev, (unsigned long) wk, |
| 78 | wk->chan, wk->chan_type, |
| 79 | wk->remain.duration, GFP_KERNEL); |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 80 | |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 81 | return WORK_ACT_NONE; |
| 82 | } |
| 83 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 84 | return WORK_ACT_TIMEOUT; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 85 | } |
| 86 | |
Johannes Berg | e5b900d | 2010-07-29 16:08:55 +0200 | [diff] [blame] | 87 | static enum work_action __must_check |
Johannes Berg | f30221e | 2010-11-25 10:02:30 +0100 | [diff] [blame] | 88 | ieee80211_offchannel_tx(struct ieee80211_work *wk) |
| 89 | { |
| 90 | if (!wk->started) { |
| 91 | wk->timeout = jiffies + msecs_to_jiffies(wk->offchan_tx.wait); |
| 92 | |
| 93 | /* |
| 94 | * After this, offchan_tx.frame remains but now is no |
| 95 | * longer a valid pointer -- we still need it as the |
Johannes Berg | 28a1bcd | 2011-10-04 18:27:10 +0200 | [diff] [blame] | 96 | * cookie for canceling this work/status matching. |
Johannes Berg | f30221e | 2010-11-25 10:02:30 +0100 | [diff] [blame] | 97 | */ |
| 98 | ieee80211_tx_skb(wk->sdata, wk->offchan_tx.frame); |
| 99 | |
| 100 | return WORK_ACT_NONE; |
| 101 | } |
| 102 | |
| 103 | return WORK_ACT_TIMEOUT; |
| 104 | } |
| 105 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 106 | static void ieee80211_work_timer(unsigned long data) |
| 107 | { |
| 108 | struct ieee80211_local *local = (void *) data; |
| 109 | |
| 110 | if (local->quiescing) |
| 111 | return; |
| 112 | |
| 113 | ieee80211_queue_work(&local->hw, &local->work_work); |
| 114 | } |
| 115 | |
| 116 | static void ieee80211_work_work(struct work_struct *work) |
| 117 | { |
| 118 | struct ieee80211_local *local = |
| 119 | container_of(work, struct ieee80211_local, work_work); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 120 | struct ieee80211_work *wk, *tmp; |
| 121 | LIST_HEAD(free_work); |
| 122 | enum work_action rma; |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 123 | bool remain_off_channel = false; |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 124 | |
| 125 | if (local->scanning) |
| 126 | return; |
| 127 | |
| 128 | /* |
| 129 | * ieee80211_queue_work() should have picked up most cases, |
Walter Goldens | 77c2061 | 2010-05-18 04:44:54 -0700 | [diff] [blame] | 130 | * here we'll pick the rest. |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 131 | */ |
| 132 | if (WARN(local->suspended, "work scheduled while going to suspend\n")) |
| 133 | return; |
| 134 | |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 135 | mutex_lock(&local->mtx); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 136 | |
Johannes Berg | 7da7cc1 | 2010-08-05 17:02:38 +0200 | [diff] [blame] | 137 | ieee80211_recalc_idle(local); |
| 138 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 139 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 140 | bool started = wk->started; |
| 141 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 142 | /* mark work as started if it's on the current off-channel */ |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 143 | if (!started && local->tmp_channel && |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 144 | wk->chan == local->tmp_channel && |
| 145 | wk->chan_type == local->tmp_channel_type) { |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 146 | started = true; |
Johannes Berg | 81ac346 | 2010-01-06 15:30:58 +0100 | [diff] [blame] | 147 | wk->timeout = jiffies; |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 148 | } |
| 149 | |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 150 | if (!started && !local->tmp_channel) { |
Johannes Berg | e76aadc | 2011-11-29 10:20:02 +0100 | [diff] [blame] | 151 | ieee80211_offchannel_stop_vifs(local, true); |
Ben Greear | da2fd1f | 2011-02-07 13:44:36 -0800 | [diff] [blame] | 152 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 153 | local->tmp_channel = wk->chan; |
Johannes Berg | e76aadc | 2011-11-29 10:20:02 +0100 | [diff] [blame] | 154 | local->tmp_channel_type = wk->chan_type; |
| 155 | |
| 156 | ieee80211_hw_config(local, 0); |
Ben Greear | b23b025 | 2011-02-04 11:54:17 -0800 | [diff] [blame] | 157 | |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 158 | started = true; |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 159 | wk->timeout = jiffies; |
| 160 | } |
| 161 | |
| 162 | /* don't try to work with items that aren't started */ |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 163 | if (!started) |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 164 | continue; |
| 165 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 166 | if (time_is_after_jiffies(wk->timeout)) { |
| 167 | /* |
| 168 | * This work item isn't supposed to be worked on |
| 169 | * right now, but take care to adjust the timer |
| 170 | * properly. |
| 171 | */ |
| 172 | run_again(local, wk->timeout); |
| 173 | continue; |
| 174 | } |
| 175 | |
| 176 | switch (wk->type) { |
| 177 | default: |
| 178 | WARN_ON(1); |
| 179 | /* nothing */ |
| 180 | rma = WORK_ACT_NONE; |
| 181 | break; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 182 | case IEEE80211_WORK_ABORT: |
| 183 | rma = WORK_ACT_TIMEOUT; |
Juuso Oikarinen | 0e0a228 | 2010-02-26 08:13:41 +0200 | [diff] [blame] | 184 | break; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 185 | case IEEE80211_WORK_REMAIN_ON_CHANNEL: |
| 186 | rma = ieee80211_remain_on_channel_timeout(wk); |
| 187 | break; |
Johannes Berg | f30221e | 2010-11-25 10:02:30 +0100 | [diff] [blame] | 188 | case IEEE80211_WORK_OFFCHANNEL_TX: |
| 189 | rma = ieee80211_offchannel_tx(wk); |
| 190 | break; |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 191 | } |
| 192 | |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 193 | wk->started = started; |
| 194 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 195 | switch (rma) { |
| 196 | case WORK_ACT_NONE: |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 197 | /* might have changed the timeout */ |
| 198 | run_again(local, wk->timeout); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 199 | break; |
| 200 | case WORK_ACT_TIMEOUT: |
| 201 | list_del_rcu(&wk->list); |
| 202 | synchronize_rcu(); |
| 203 | list_add(&wk->list, &free_work); |
| 204 | break; |
| 205 | default: |
| 206 | WARN(1, "unexpected: %d", rma); |
| 207 | } |
| 208 | } |
| 209 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 210 | list_for_each_entry(wk, &local->work_list, list) { |
| 211 | if (!wk->started) |
| 212 | continue; |
Johannes Berg | e76aadc | 2011-11-29 10:20:02 +0100 | [diff] [blame] | 213 | if (wk->chan != local->tmp_channel || |
| 214 | wk->chan_type != local->tmp_channel_type) |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 215 | continue; |
| 216 | remain_off_channel = true; |
| 217 | } |
| 218 | |
| 219 | if (!remain_off_channel && local->tmp_channel) { |
| 220 | local->tmp_channel = NULL; |
Johannes Berg | e76aadc | 2011-11-29 10:20:02 +0100 | [diff] [blame] | 221 | ieee80211_hw_config(local, 0); |
Ben Greear | b23b025 | 2011-02-04 11:54:17 -0800 | [diff] [blame] | 222 | |
Johannes Berg | e76aadc | 2011-11-29 10:20:02 +0100 | [diff] [blame] | 223 | ieee80211_offchannel_return(local, true); |
Ben Greear | b23b025 | 2011-02-04 11:54:17 -0800 | [diff] [blame] | 224 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 225 | /* give connection some time to breathe */ |
| 226 | run_again(local, jiffies + HZ/2); |
| 227 | } |
| 228 | |
Teemu Paasikivi | 68dd5b7 | 2010-04-09 13:07:55 +0300 | [diff] [blame] | 229 | if (list_empty(&local->work_list) && local->scan_req && |
| 230 | !local->scanning) |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 231 | ieee80211_queue_delayed_work(&local->hw, |
| 232 | &local->scan_work, |
| 233 | round_jiffies_relative(0)); |
| 234 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 235 | ieee80211_recalc_idle(local); |
| 236 | |
Johannes Berg | 7da7cc1 | 2010-08-05 17:02:38 +0200 | [diff] [blame] | 237 | mutex_unlock(&local->mtx); |
| 238 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 239 | list_for_each_entry_safe(wk, tmp, &free_work, list) { |
| 240 | wk->done(wk, NULL); |
| 241 | list_del(&wk->list); |
| 242 | kfree(wk); |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | void ieee80211_add_work(struct ieee80211_work *wk) |
| 247 | { |
| 248 | struct ieee80211_local *local; |
| 249 | |
| 250 | if (WARN_ON(!wk->chan)) |
| 251 | return; |
| 252 | |
| 253 | if (WARN_ON(!wk->sdata)) |
| 254 | return; |
| 255 | |
| 256 | if (WARN_ON(!wk->done)) |
| 257 | return; |
| 258 | |
Johannes Berg | 81ac346 | 2010-01-06 15:30:58 +0100 | [diff] [blame] | 259 | if (WARN_ON(!ieee80211_sdata_running(wk->sdata))) |
| 260 | return; |
| 261 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 262 | wk->started = false; |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 263 | |
| 264 | local = wk->sdata->local; |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 265 | mutex_lock(&local->mtx); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 266 | list_add_tail(&wk->list, &local->work_list); |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 267 | mutex_unlock(&local->mtx); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 268 | |
| 269 | ieee80211_queue_work(&local->hw, &local->work_work); |
| 270 | } |
| 271 | |
| 272 | void ieee80211_work_init(struct ieee80211_local *local) |
| 273 | { |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 274 | INIT_LIST_HEAD(&local->work_list); |
| 275 | setup_timer(&local->work_timer, ieee80211_work_timer, |
| 276 | (unsigned long)local); |
| 277 | INIT_WORK(&local->work_work, ieee80211_work_work); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 278 | } |
| 279 | |
| 280 | void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) |
| 281 | { |
| 282 | struct ieee80211_local *local = sdata->local; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 283 | struct ieee80211_work *wk; |
Herton Ronaldo Krzesinski | 8808f64 | 2010-12-13 11:43:51 -0200 | [diff] [blame] | 284 | bool cleanup = false; |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 285 | |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 286 | mutex_lock(&local->mtx); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 287 | list_for_each_entry(wk, &local->work_list, list) { |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 288 | if (wk->sdata != sdata) |
| 289 | continue; |
Herton Ronaldo Krzesinski | 8808f64 | 2010-12-13 11:43:51 -0200 | [diff] [blame] | 290 | cleanup = true; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 291 | wk->type = IEEE80211_WORK_ABORT; |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 292 | wk->started = true; |
| 293 | wk->timeout = jiffies; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 294 | } |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 295 | mutex_unlock(&local->mtx); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 296 | |
| 297 | /* run cleanups etc. */ |
Herton Ronaldo Krzesinski | 8808f64 | 2010-12-13 11:43:51 -0200 | [diff] [blame] | 298 | if (cleanup) |
| 299 | ieee80211_work_work(&local->work_work); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 300 | |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 301 | mutex_lock(&local->mtx); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 302 | list_for_each_entry(wk, &local->work_list, list) { |
| 303 | if (wk->sdata != sdata) |
| 304 | continue; |
| 305 | WARN_ON(1); |
| 306 | break; |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 307 | } |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 308 | mutex_unlock(&local->mtx); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 309 | } |
| 310 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 311 | static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk, |
| 312 | struct sk_buff *skb) |
| 313 | { |
| 314 | /* |
| 315 | * We are done serving the remain-on-channel command. |
| 316 | */ |
Kalle Valo | 1990ca6 | 2009-12-30 14:42:20 +0200 | [diff] [blame] | 317 | cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk, |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 318 | wk->chan, wk->chan_type, |
| 319 | GFP_KERNEL); |
| 320 | |
| 321 | return WORK_DONE_DESTROY; |
| 322 | } |
| 323 | |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 324 | int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, |
| 325 | struct ieee80211_channel *chan, |
| 326 | enum nl80211_channel_type channel_type, |
| 327 | unsigned int duration, u64 *cookie) |
| 328 | { |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 329 | struct ieee80211_work *wk; |
| 330 | |
| 331 | wk = kzalloc(sizeof(*wk), GFP_KERNEL); |
| 332 | if (!wk) |
| 333 | return -ENOMEM; |
| 334 | |
| 335 | wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL; |
| 336 | wk->chan = chan; |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 337 | wk->chan_type = channel_type; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 338 | wk->sdata = sdata; |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 339 | wk->done = ieee80211_remain_done; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 340 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 341 | wk->remain.duration = duration; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 342 | |
Kalle Valo | 1990ca6 | 2009-12-30 14:42:20 +0200 | [diff] [blame] | 343 | *cookie = (unsigned long) wk; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 344 | |
| 345 | ieee80211_add_work(wk); |
| 346 | |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 347 | return 0; |
| 348 | } |
| 349 | |
| 350 | int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata, |
| 351 | u64 cookie) |
| 352 | { |
| 353 | struct ieee80211_local *local = sdata->local; |
| 354 | struct ieee80211_work *wk, *tmp; |
| 355 | bool found = false; |
| 356 | |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 357 | mutex_lock(&local->mtx); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 358 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { |
Kalle Valo | 1990ca6 | 2009-12-30 14:42:20 +0200 | [diff] [blame] | 359 | if ((unsigned long) wk == cookie) { |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 360 | wk->timeout = jiffies; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 361 | found = true; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 362 | break; |
| 363 | } |
| 364 | } |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 365 | mutex_unlock(&local->mtx); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 366 | |
| 367 | if (!found) |
| 368 | return -ENOENT; |
| 369 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 370 | ieee80211_queue_work(&local->hw, &local->work_work); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 371 | |
| 372 | return 0; |
| 373 | } |