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 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 125 | /* |
| 126 | * ieee80211_queue_work() should have picked up most cases, |
Walter Goldens | 77c2061 | 2010-05-18 04:44:54 -0700 | [diff] [blame] | 127 | * here we'll pick the rest. |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 128 | */ |
| 129 | if (WARN(local->suspended, "work scheduled while going to suspend\n")) |
| 130 | return; |
| 131 | |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 132 | mutex_lock(&local->mtx); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 133 | |
Stanislaw Gruszka | 5f54607 | 2012-03-28 16:01:20 +0200 | [diff] [blame] | 134 | if (local->scanning) { |
| 135 | mutex_unlock(&local->mtx); |
| 136 | return; |
| 137 | } |
| 138 | |
Johannes Berg | 7da7cc1 | 2010-08-05 17:02:38 +0200 | [diff] [blame] | 139 | ieee80211_recalc_idle(local); |
| 140 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 141 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 142 | bool started = wk->started; |
| 143 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 144 | /* mark work as started if it's on the current off-channel */ |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 145 | if (!started && local->tmp_channel && |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 146 | wk->chan == local->tmp_channel && |
| 147 | wk->chan_type == local->tmp_channel_type) { |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 148 | started = true; |
Johannes Berg | 81ac346 | 2010-01-06 15:30:58 +0100 | [diff] [blame] | 149 | wk->timeout = jiffies; |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 150 | } |
| 151 | |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 152 | if (!started && !local->tmp_channel) { |
Johannes Berg | e76aadc | 2011-11-29 10:20:02 +0100 | [diff] [blame] | 153 | ieee80211_offchannel_stop_vifs(local, true); |
Ben Greear | da2fd1f | 2011-02-07 13:44:36 -0800 | [diff] [blame] | 154 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 155 | local->tmp_channel = wk->chan; |
Johannes Berg | e76aadc | 2011-11-29 10:20:02 +0100 | [diff] [blame] | 156 | local->tmp_channel_type = wk->chan_type; |
| 157 | |
| 158 | ieee80211_hw_config(local, 0); |
Ben Greear | b23b025 | 2011-02-04 11:54:17 -0800 | [diff] [blame] | 159 | |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 160 | started = true; |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 161 | wk->timeout = jiffies; |
| 162 | } |
| 163 | |
| 164 | /* don't try to work with items that aren't started */ |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 165 | if (!started) |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 166 | continue; |
| 167 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 168 | if (time_is_after_jiffies(wk->timeout)) { |
| 169 | /* |
| 170 | * This work item isn't supposed to be worked on |
| 171 | * right now, but take care to adjust the timer |
| 172 | * properly. |
| 173 | */ |
| 174 | run_again(local, wk->timeout); |
| 175 | continue; |
| 176 | } |
| 177 | |
| 178 | switch (wk->type) { |
| 179 | default: |
| 180 | WARN_ON(1); |
| 181 | /* nothing */ |
| 182 | rma = WORK_ACT_NONE; |
| 183 | break; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 184 | case IEEE80211_WORK_ABORT: |
| 185 | rma = WORK_ACT_TIMEOUT; |
Juuso Oikarinen | 0e0a228 | 2010-02-26 08:13:41 +0200 | [diff] [blame] | 186 | break; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 187 | case IEEE80211_WORK_REMAIN_ON_CHANNEL: |
| 188 | rma = ieee80211_remain_on_channel_timeout(wk); |
| 189 | break; |
Johannes Berg | f30221e | 2010-11-25 10:02:30 +0100 | [diff] [blame] | 190 | case IEEE80211_WORK_OFFCHANNEL_TX: |
| 191 | rma = ieee80211_offchannel_tx(wk); |
| 192 | break; |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 193 | } |
| 194 | |
Johannes Berg | 723bae7 | 2010-01-25 13:36:36 +0100 | [diff] [blame] | 195 | wk->started = started; |
| 196 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 197 | switch (rma) { |
| 198 | case WORK_ACT_NONE: |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 199 | /* might have changed the timeout */ |
| 200 | run_again(local, wk->timeout); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 201 | break; |
| 202 | case WORK_ACT_TIMEOUT: |
| 203 | list_del_rcu(&wk->list); |
| 204 | synchronize_rcu(); |
| 205 | list_add(&wk->list, &free_work); |
| 206 | break; |
| 207 | default: |
| 208 | WARN(1, "unexpected: %d", rma); |
| 209 | } |
| 210 | } |
| 211 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 212 | list_for_each_entry(wk, &local->work_list, list) { |
| 213 | if (!wk->started) |
| 214 | continue; |
Johannes Berg | e76aadc | 2011-11-29 10:20:02 +0100 | [diff] [blame] | 215 | if (wk->chan != local->tmp_channel || |
| 216 | wk->chan_type != local->tmp_channel_type) |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 217 | continue; |
| 218 | remain_off_channel = true; |
| 219 | } |
| 220 | |
| 221 | if (!remain_off_channel && local->tmp_channel) { |
| 222 | local->tmp_channel = NULL; |
Johannes Berg | e76aadc | 2011-11-29 10:20:02 +0100 | [diff] [blame] | 223 | ieee80211_hw_config(local, 0); |
Ben Greear | b23b025 | 2011-02-04 11:54:17 -0800 | [diff] [blame] | 224 | |
Johannes Berg | e76aadc | 2011-11-29 10:20:02 +0100 | [diff] [blame] | 225 | ieee80211_offchannel_return(local, true); |
Ben Greear | b23b025 | 2011-02-04 11:54:17 -0800 | [diff] [blame] | 226 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 227 | /* give connection some time to breathe */ |
| 228 | run_again(local, jiffies + HZ/2); |
| 229 | } |
| 230 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 231 | ieee80211_recalc_idle(local); |
Stanislaw Gruszka | 133d40f | 2012-03-28 16:01:19 +0200 | [diff] [blame] | 232 | ieee80211_run_deferred_scan(local); |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 233 | |
Johannes Berg | 7da7cc1 | 2010-08-05 17:02:38 +0200 | [diff] [blame] | 234 | mutex_unlock(&local->mtx); |
| 235 | |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 236 | list_for_each_entry_safe(wk, tmp, &free_work, list) { |
| 237 | wk->done(wk, NULL); |
| 238 | list_del(&wk->list); |
| 239 | kfree(wk); |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | void ieee80211_add_work(struct ieee80211_work *wk) |
| 244 | { |
| 245 | struct ieee80211_local *local; |
| 246 | |
| 247 | if (WARN_ON(!wk->chan)) |
| 248 | return; |
| 249 | |
| 250 | if (WARN_ON(!wk->sdata)) |
| 251 | return; |
| 252 | |
| 253 | if (WARN_ON(!wk->done)) |
| 254 | return; |
| 255 | |
Johannes Berg | 81ac346 | 2010-01-06 15:30:58 +0100 | [diff] [blame] | 256 | if (WARN_ON(!ieee80211_sdata_running(wk->sdata))) |
| 257 | return; |
| 258 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 259 | wk->started = false; |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 260 | |
| 261 | local = wk->sdata->local; |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 262 | mutex_lock(&local->mtx); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 263 | list_add_tail(&wk->list, &local->work_list); |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 264 | mutex_unlock(&local->mtx); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 265 | |
| 266 | ieee80211_queue_work(&local->hw, &local->work_work); |
| 267 | } |
| 268 | |
| 269 | void ieee80211_work_init(struct ieee80211_local *local) |
| 270 | { |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 271 | INIT_LIST_HEAD(&local->work_list); |
| 272 | setup_timer(&local->work_timer, ieee80211_work_timer, |
| 273 | (unsigned long)local); |
| 274 | INIT_WORK(&local->work_work, ieee80211_work_work); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 275 | } |
| 276 | |
| 277 | void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) |
| 278 | { |
| 279 | struct ieee80211_local *local = sdata->local; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 280 | struct ieee80211_work *wk; |
Herton Ronaldo Krzesinski | 8808f64 | 2010-12-13 11:43:51 -0200 | [diff] [blame] | 281 | bool cleanup = false; |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 282 | |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 283 | mutex_lock(&local->mtx); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 284 | list_for_each_entry(wk, &local->work_list, list) { |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 285 | if (wk->sdata != sdata) |
| 286 | continue; |
Herton Ronaldo Krzesinski | 8808f64 | 2010-12-13 11:43:51 -0200 | [diff] [blame] | 287 | cleanup = true; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 288 | wk->type = IEEE80211_WORK_ABORT; |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 289 | wk->started = true; |
| 290 | wk->timeout = jiffies; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 291 | } |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 292 | mutex_unlock(&local->mtx); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 293 | |
| 294 | /* run cleanups etc. */ |
Herton Ronaldo Krzesinski | 8808f64 | 2010-12-13 11:43:51 -0200 | [diff] [blame] | 295 | if (cleanup) |
| 296 | ieee80211_work_work(&local->work_work); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 297 | |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 298 | mutex_lock(&local->mtx); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 299 | list_for_each_entry(wk, &local->work_list, list) { |
| 300 | if (wk->sdata != sdata) |
| 301 | continue; |
| 302 | WARN_ON(1); |
| 303 | break; |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 304 | } |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 305 | mutex_unlock(&local->mtx); |
Johannes Berg | af6b637 | 2009-12-23 13:15:35 +0100 | [diff] [blame] | 306 | } |
| 307 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 308 | static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk, |
| 309 | struct sk_buff *skb) |
| 310 | { |
| 311 | /* |
| 312 | * We are done serving the remain-on-channel command. |
| 313 | */ |
Kalle Valo | 1990ca6 | 2009-12-30 14:42:20 +0200 | [diff] [blame] | 314 | cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk, |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 315 | wk->chan, wk->chan_type, |
| 316 | GFP_KERNEL); |
| 317 | |
| 318 | return WORK_DONE_DESTROY; |
| 319 | } |
| 320 | |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 321 | int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, |
| 322 | struct ieee80211_channel *chan, |
| 323 | enum nl80211_channel_type channel_type, |
| 324 | unsigned int duration, u64 *cookie) |
| 325 | { |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 326 | struct ieee80211_work *wk; |
| 327 | |
| 328 | wk = kzalloc(sizeof(*wk), GFP_KERNEL); |
| 329 | if (!wk) |
| 330 | return -ENOMEM; |
| 331 | |
| 332 | wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL; |
| 333 | wk->chan = chan; |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 334 | wk->chan_type = channel_type; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 335 | wk->sdata = sdata; |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 336 | wk->done = ieee80211_remain_done; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 337 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 338 | wk->remain.duration = duration; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 339 | |
Kalle Valo | 1990ca6 | 2009-12-30 14:42:20 +0200 | [diff] [blame] | 340 | *cookie = (unsigned long) wk; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 341 | |
| 342 | ieee80211_add_work(wk); |
| 343 | |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 344 | return 0; |
| 345 | } |
| 346 | |
| 347 | int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata, |
| 348 | u64 cookie) |
| 349 | { |
| 350 | struct ieee80211_local *local = sdata->local; |
| 351 | struct ieee80211_work *wk, *tmp; |
| 352 | bool found = false; |
| 353 | |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 354 | mutex_lock(&local->mtx); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 355 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { |
Kalle Valo | 1990ca6 | 2009-12-30 14:42:20 +0200 | [diff] [blame] | 356 | if ((unsigned long) wk == cookie) { |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 357 | wk->timeout = jiffies; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 358 | found = true; |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 359 | break; |
| 360 | } |
| 361 | } |
Johannes Berg | a1699b7 | 2010-07-30 16:46:07 +0200 | [diff] [blame] | 362 | mutex_unlock(&local->mtx); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 363 | |
| 364 | if (!found) |
| 365 | return -ENOENT; |
| 366 | |
Johannes Berg | e4da8c3 | 2009-12-23 13:15:43 +0100 | [diff] [blame] | 367 | ieee80211_queue_work(&local->hw, &local->work_work); |
Johannes Berg | b8bc4b0 | 2009-12-23 13:15:42 +0100 | [diff] [blame] | 368 | |
| 369 | return 0; |
| 370 | } |