diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/Makefile | 1 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 478 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 89 | ||||
-rw-r--r-- | net/mac80211/iface.c | 23 | ||||
-rw-r--r-- | net/mac80211/main.c | 10 | ||||
-rw-r--r-- | net/mac80211/offchannel.c | 280 | ||||
-rw-r--r-- | net/mac80211/scan.c | 4 | ||||
-rw-r--r-- | net/mac80211/status.c | 28 | ||||
-rw-r--r-- | net/mac80211/work.c | 370 |
9 files changed, 532 insertions, 751 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 3e9d931bba35..2b1470bac178 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile | |||
@@ -9,7 +9,6 @@ mac80211-y := \ | |||
9 | scan.o offchannel.o \ | 9 | scan.o offchannel.o \ |
10 | ht.o agg-tx.o agg-rx.o \ | 10 | ht.o agg-tx.o agg-rx.o \ |
11 | ibss.o \ | 11 | ibss.o \ |
12 | work.o \ | ||
13 | iface.o \ | 12 | iface.o \ |
14 | rate.o \ | 13 | rate.o \ |
15 | michael.o \ | 14 | michael.o \ |
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a16907919709..498c94e34427 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -2112,35 +2112,171 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, | |||
2112 | return 0; | 2112 | return 0; |
2113 | } | 2113 | } |
2114 | 2114 | ||
2115 | static int ieee80211_remain_on_channel_hw(struct ieee80211_local *local, | 2115 | static int ieee80211_start_roc_work(struct ieee80211_local *local, |
2116 | struct net_device *dev, | 2116 | struct ieee80211_sub_if_data *sdata, |
2117 | struct ieee80211_channel *chan, | 2117 | struct ieee80211_channel *channel, |
2118 | enum nl80211_channel_type chantype, | 2118 | enum nl80211_channel_type channel_type, |
2119 | unsigned int duration, u64 *cookie) | 2119 | unsigned int duration, u64 *cookie, |
2120 | { | 2120 | struct sk_buff *txskb) |
2121 | { | ||
2122 | struct ieee80211_roc_work *roc, *tmp; | ||
2123 | bool queued = false; | ||
2121 | int ret; | 2124 | int ret; |
2122 | u32 random_cookie; | ||
2123 | 2125 | ||
2124 | lockdep_assert_held(&local->mtx); | 2126 | lockdep_assert_held(&local->mtx); |
2125 | 2127 | ||
2126 | if (local->hw_roc_cookie) | 2128 | roc = kzalloc(sizeof(*roc), GFP_KERNEL); |
2127 | return -EBUSY; | 2129 | if (!roc) |
2128 | /* must be nonzero */ | 2130 | return -ENOMEM; |
2129 | random_cookie = random32() | 1; | 2131 | |
2130 | 2132 | roc->chan = channel; | |
2131 | *cookie = random_cookie; | 2133 | roc->chan_type = channel_type; |
2132 | local->hw_roc_dev = dev; | 2134 | roc->duration = duration; |
2133 | local->hw_roc_cookie = random_cookie; | 2135 | roc->req_duration = duration; |
2134 | local->hw_roc_channel = chan; | 2136 | roc->frame = txskb; |
2135 | local->hw_roc_channel_type = chantype; | 2137 | roc->mgmt_tx_cookie = (unsigned long)txskb; |
2136 | local->hw_roc_duration = duration; | 2138 | roc->sdata = sdata; |
2137 | ret = drv_remain_on_channel(local, chan, chantype, duration); | 2139 | INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work); |
2140 | INIT_LIST_HEAD(&roc->dependents); | ||
2141 | |||
2142 | /* if there's one pending or we're scanning, queue this one */ | ||
2143 | if (!list_empty(&local->roc_list) || local->scanning) | ||
2144 | goto out_check_combine; | ||
2145 | |||
2146 | /* if not HW assist, just queue & schedule work */ | ||
2147 | if (!local->ops->remain_on_channel) { | ||
2148 | ieee80211_queue_delayed_work(&local->hw, &roc->work, 0); | ||
2149 | goto out_queue; | ||
2150 | } | ||
2151 | |||
2152 | /* otherwise actually kick it off here (for error handling) */ | ||
2153 | |||
2154 | /* | ||
2155 | * If the duration is zero, then the driver | ||
2156 | * wouldn't actually do anything. Set it to | ||
2157 | * 10 for now. | ||
2158 | * | ||
2159 | * TODO: cancel the off-channel operation | ||
2160 | * when we get the SKB's TX status and | ||
2161 | * the wait time was zero before. | ||
2162 | */ | ||
2163 | if (!duration) | ||
2164 | duration = 10; | ||
2165 | |||
2166 | ret = drv_remain_on_channel(local, channel, channel_type, duration); | ||
2138 | if (ret) { | 2167 | if (ret) { |
2139 | local->hw_roc_channel = NULL; | 2168 | kfree(roc); |
2140 | local->hw_roc_cookie = 0; | 2169 | return ret; |
2141 | } | 2170 | } |
2142 | 2171 | ||
2143 | return ret; | 2172 | roc->started = true; |
2173 | goto out_queue; | ||
2174 | |||
2175 | out_check_combine: | ||
2176 | list_for_each_entry(tmp, &local->roc_list, list) { | ||
2177 | if (tmp->chan != channel || tmp->chan_type != channel_type) | ||
2178 | continue; | ||
2179 | |||
2180 | /* | ||
2181 | * Extend this ROC if possible: | ||
2182 | * | ||
2183 | * If it hasn't started yet, just increase the duration | ||
2184 | * and add the new one to the list of dependents. | ||
2185 | */ | ||
2186 | if (!tmp->started) { | ||
2187 | list_add_tail(&roc->list, &tmp->dependents); | ||
2188 | tmp->duration = max(tmp->duration, roc->duration); | ||
2189 | queued = true; | ||
2190 | break; | ||
2191 | } | ||
2192 | |||
2193 | /* If it has already started, it's more difficult ... */ | ||
2194 | if (local->ops->remain_on_channel) { | ||
2195 | unsigned long j = jiffies; | ||
2196 | |||
2197 | /* | ||
2198 | * In the offloaded ROC case, if it hasn't begun, add | ||
2199 | * this new one to the dependent list to be handled | ||
2200 | * when the the master one begins. If it has begun, | ||
2201 | * check that there's still a minimum time left and | ||
2202 | * if so, start this one, transmitting the frame, but | ||
2203 | * add it to the list directly after this one with a | ||
2204 | * a reduced time so we'll ask the driver to execute | ||
2205 | * it right after finishing the previous one, in the | ||
2206 | * hope that it'll also be executed right afterwards, | ||
2207 | * effectively extending the old one. | ||
2208 | * If there's no minimum time left, just add it to the | ||
2209 | * normal list. | ||
2210 | */ | ||
2211 | if (!tmp->hw_begun) { | ||
2212 | list_add_tail(&roc->list, &tmp->dependents); | ||
2213 | queued = true; | ||
2214 | break; | ||
2215 | } | ||
2216 | |||
2217 | if (time_before(j + IEEE80211_ROC_MIN_LEFT, | ||
2218 | tmp->hw_start_time + | ||
2219 | msecs_to_jiffies(tmp->duration))) { | ||
2220 | int new_dur; | ||
2221 | |||
2222 | ieee80211_handle_roc_started(roc); | ||
2223 | |||
2224 | new_dur = roc->duration - | ||
2225 | jiffies_to_msecs(tmp->hw_start_time + | ||
2226 | msecs_to_jiffies( | ||
2227 | tmp->duration) - | ||
2228 | j); | ||
2229 | |||
2230 | if (new_dur > 0) { | ||
2231 | /* add right after tmp */ | ||
2232 | list_add(&roc->list, &tmp->list); | ||
2233 | } else { | ||
2234 | list_add_tail(&roc->list, | ||
2235 | &tmp->dependents); | ||
2236 | } | ||
2237 | queued = true; | ||
2238 | } | ||
2239 | } else if (del_timer_sync(&tmp->work.timer)) { | ||
2240 | unsigned long new_end; | ||
2241 | |||
2242 | /* | ||
2243 | * In the software ROC case, cancel the timer, if | ||
2244 | * that fails then the finish work is already | ||
2245 | * queued/pending and thus we queue the new ROC | ||
2246 | * normally, if that succeeds then we can extend | ||
2247 | * the timer duration and TX the frame (if any.) | ||
2248 | */ | ||
2249 | |||
2250 | list_add_tail(&roc->list, &tmp->dependents); | ||
2251 | queued = true; | ||
2252 | |||
2253 | new_end = jiffies + msecs_to_jiffies(roc->duration); | ||
2254 | |||
2255 | /* ok, it was started & we canceled timer */ | ||
2256 | if (time_after(new_end, tmp->work.timer.expires)) | ||
2257 | mod_timer(&tmp->work.timer, new_end); | ||
2258 | else | ||
2259 | add_timer(&tmp->work.timer); | ||
2260 | |||
2261 | ieee80211_handle_roc_started(roc); | ||
2262 | } | ||
2263 | break; | ||
2264 | } | ||
2265 | |||
2266 | out_queue: | ||
2267 | if (!queued) | ||
2268 | list_add_tail(&roc->list, &local->roc_list); | ||
2269 | |||
2270 | /* | ||
2271 | * cookie is either the roc (for normal roc) | ||
2272 | * or the SKB (for mgmt TX) | ||
2273 | */ | ||
2274 | if (txskb) | ||
2275 | *cookie = (unsigned long)txskb; | ||
2276 | else | ||
2277 | *cookie = (unsigned long)roc; | ||
2278 | |||
2279 | return 0; | ||
2144 | } | 2280 | } |
2145 | 2281 | ||
2146 | static int ieee80211_remain_on_channel(struct wiphy *wiphy, | 2282 | static int ieee80211_remain_on_channel(struct wiphy *wiphy, |
@@ -2152,84 +2288,76 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy, | |||
2152 | { | 2288 | { |
2153 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 2289 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
2154 | struct ieee80211_local *local = sdata->local; | 2290 | struct ieee80211_local *local = sdata->local; |
2291 | int ret; | ||
2155 | 2292 | ||
2156 | if (local->ops->remain_on_channel) { | 2293 | mutex_lock(&local->mtx); |
2157 | int ret; | 2294 | ret = ieee80211_start_roc_work(local, sdata, chan, channel_type, |
2158 | 2295 | duration, cookie, NULL); | |
2159 | mutex_lock(&local->mtx); | 2296 | mutex_unlock(&local->mtx); |
2160 | ret = ieee80211_remain_on_channel_hw(local, dev, | ||
2161 | chan, channel_type, | ||
2162 | duration, cookie); | ||
2163 | local->hw_roc_for_tx = false; | ||
2164 | mutex_unlock(&local->mtx); | ||
2165 | |||
2166 | return ret; | ||
2167 | } | ||
2168 | 2297 | ||
2169 | return ieee80211_wk_remain_on_channel(sdata, chan, channel_type, | 2298 | return ret; |
2170 | duration, cookie); | ||
2171 | } | 2299 | } |
2172 | 2300 | ||
2173 | static int ieee80211_cancel_remain_on_channel_hw(struct ieee80211_local *local, | 2301 | static int ieee80211_cancel_roc(struct ieee80211_local *local, |
2174 | u64 cookie) | 2302 | u64 cookie, bool mgmt_tx) |
2175 | { | 2303 | { |
2304 | struct ieee80211_roc_work *roc, *tmp, *found = NULL; | ||
2176 | int ret; | 2305 | int ret; |
2177 | 2306 | ||
2178 | lockdep_assert_held(&local->mtx); | 2307 | mutex_lock(&local->mtx); |
2308 | list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { | ||
2309 | if (!mgmt_tx && (unsigned long)roc != cookie) | ||
2310 | continue; | ||
2311 | else if (mgmt_tx && roc->mgmt_tx_cookie != cookie) | ||
2312 | continue; | ||
2179 | 2313 | ||
2180 | if (local->hw_roc_cookie != cookie) | 2314 | found = roc; |
2181 | return -ENOENT; | 2315 | break; |
2316 | } | ||
2182 | 2317 | ||
2183 | ret = drv_cancel_remain_on_channel(local); | 2318 | if (!found) { |
2184 | if (ret) | 2319 | mutex_unlock(&local->mtx); |
2185 | return ret; | 2320 | return -ENOENT; |
2321 | } | ||
2186 | 2322 | ||
2187 | local->hw_roc_cookie = 0; | 2323 | if (local->ops->remain_on_channel) { |
2188 | local->hw_roc_channel = NULL; | 2324 | if (found->started) { |
2325 | ret = drv_cancel_remain_on_channel(local); | ||
2326 | if (WARN_ON_ONCE(ret)) { | ||
2327 | mutex_unlock(&local->mtx); | ||
2328 | return ret; | ||
2329 | } | ||
2330 | } | ||
2189 | 2331 | ||
2190 | return 0; | 2332 | list_del(&found->list); |
2191 | } | ||
2192 | 2333 | ||
2193 | static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, | 2334 | ieee80211_run_deferred_scan(local); |
2194 | struct net_device *dev, | 2335 | ieee80211_start_next_roc(local); |
2195 | u64 cookie) | 2336 | mutex_unlock(&local->mtx); |
2196 | { | ||
2197 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
2198 | struct ieee80211_local *local = sdata->local; | ||
2199 | 2337 | ||
2200 | if (local->ops->cancel_remain_on_channel) { | 2338 | ieee80211_roc_notify_destroy(found); |
2201 | int ret; | 2339 | } else { |
2340 | /* work may be pending so use it all the time */ | ||
2341 | found->abort = true; | ||
2342 | ieee80211_queue_delayed_work(&local->hw, &found->work, 0); | ||
2202 | 2343 | ||
2203 | mutex_lock(&local->mtx); | ||
2204 | ret = ieee80211_cancel_remain_on_channel_hw(local, cookie); | ||
2205 | mutex_unlock(&local->mtx); | 2344 | mutex_unlock(&local->mtx); |
2206 | 2345 | ||
2207 | return ret; | 2346 | /* work will clean up etc */ |
2347 | flush_delayed_work(&found->work); | ||
2208 | } | 2348 | } |
2209 | 2349 | ||
2210 | return ieee80211_wk_cancel_remain_on_channel(sdata, cookie); | 2350 | return 0; |
2211 | } | 2351 | } |
2212 | 2352 | ||
2213 | static enum work_done_result | 2353 | static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, |
2214 | ieee80211_offchan_tx_done(struct ieee80211_work *wk, struct sk_buff *skb) | 2354 | struct net_device *dev, |
2355 | u64 cookie) | ||
2215 | { | 2356 | { |
2216 | /* | 2357 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
2217 | * Use the data embedded in the work struct for reporting | 2358 | struct ieee80211_local *local = sdata->local; |
2218 | * here so if the driver mangled the SKB before dropping | ||
2219 | * it (which is the only way we really should get here) | ||
2220 | * then we don't report mangled data. | ||
2221 | * | ||
2222 | * If there was no wait time, then by the time we get here | ||
2223 | * the driver will likely not have reported the status yet, | ||
2224 | * so in that case userspace will have to deal with it. | ||
2225 | */ | ||
2226 | |||
2227 | if (wk->offchan_tx.wait && !wk->offchan_tx.status) | ||
2228 | cfg80211_mgmt_tx_status(wk->sdata->dev, | ||
2229 | (unsigned long) wk->offchan_tx.frame, | ||
2230 | wk->data, wk->data_len, false, GFP_KERNEL); | ||
2231 | 2359 | ||
2232 | return WORK_DONE_DESTROY; | 2360 | return ieee80211_cancel_roc(local, cookie, false); |
2233 | } | 2361 | } |
2234 | 2362 | ||
2235 | static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, | 2363 | static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, |
@@ -2243,10 +2371,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, | |||
2243 | struct ieee80211_local *local = sdata->local; | 2371 | struct ieee80211_local *local = sdata->local; |
2244 | struct sk_buff *skb; | 2372 | struct sk_buff *skb; |
2245 | struct sta_info *sta; | 2373 | struct sta_info *sta; |
2246 | struct ieee80211_work *wk; | ||
2247 | const struct ieee80211_mgmt *mgmt = (void *)buf; | 2374 | const struct ieee80211_mgmt *mgmt = (void *)buf; |
2375 | bool need_offchan = false; | ||
2248 | u32 flags; | 2376 | u32 flags; |
2249 | bool is_offchan = false, in_hw_roc = false; | 2377 | int ret; |
2250 | 2378 | ||
2251 | if (dont_wait_for_ack) | 2379 | if (dont_wait_for_ack) |
2252 | flags = IEEE80211_TX_CTL_NO_ACK; | 2380 | flags = IEEE80211_TX_CTL_NO_ACK; |
@@ -2254,34 +2382,28 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, | |||
2254 | flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | | 2382 | flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | |
2255 | IEEE80211_TX_CTL_REQ_TX_STATUS; | 2383 | IEEE80211_TX_CTL_REQ_TX_STATUS; |
2256 | 2384 | ||
2257 | /* Check that we are on the requested channel for transmission */ | ||
2258 | if (chan != local->tmp_channel && | ||
2259 | chan != local->oper_channel) | ||
2260 | is_offchan = true; | ||
2261 | if (channel_type_valid && | ||
2262 | (channel_type != local->tmp_channel_type && | ||
2263 | channel_type != local->_oper_channel_type)) | ||
2264 | is_offchan = true; | ||
2265 | |||
2266 | if (chan == local->hw_roc_channel) { | ||
2267 | /* TODO: check channel type? */ | ||
2268 | is_offchan = false; | ||
2269 | in_hw_roc = true; | ||
2270 | flags |= IEEE80211_TX_CTL_TX_OFFCHAN; | ||
2271 | } | ||
2272 | |||
2273 | if (no_cck) | 2385 | if (no_cck) |
2274 | flags |= IEEE80211_TX_CTL_NO_CCK_RATE; | 2386 | flags |= IEEE80211_TX_CTL_NO_CCK_RATE; |
2275 | 2387 | ||
2276 | if (is_offchan && !offchan) | ||
2277 | return -EBUSY; | ||
2278 | |||
2279 | switch (sdata->vif.type) { | 2388 | switch (sdata->vif.type) { |
2280 | case NL80211_IFTYPE_ADHOC: | 2389 | case NL80211_IFTYPE_ADHOC: |
2390 | if (!sdata->vif.bss_conf.ibss_joined) | ||
2391 | need_offchan = true; | ||
2392 | /* fall through */ | ||
2393 | #ifdef CONFIG_MAC80211_MESH | ||
2394 | case NL80211_IFTYPE_MESH_POINT: | ||
2395 | if (ieee80211_vif_is_mesh(&sdata->vif) && | ||
2396 | !sdata->u.mesh.mesh_id_len) | ||
2397 | need_offchan = true; | ||
2398 | /* fall through */ | ||
2399 | #endif | ||
2281 | case NL80211_IFTYPE_AP: | 2400 | case NL80211_IFTYPE_AP: |
2282 | case NL80211_IFTYPE_AP_VLAN: | 2401 | case NL80211_IFTYPE_AP_VLAN: |
2283 | case NL80211_IFTYPE_P2P_GO: | 2402 | case NL80211_IFTYPE_P2P_GO: |
2284 | case NL80211_IFTYPE_MESH_POINT: | 2403 | if (sdata->vif.type != NL80211_IFTYPE_ADHOC && |
2404 | !ieee80211_vif_is_mesh(&sdata->vif) && | ||
2405 | !rcu_access_pointer(sdata->bss->beacon)) | ||
2406 | need_offchan = true; | ||
2285 | if (!ieee80211_is_action(mgmt->frame_control) || | 2407 | if (!ieee80211_is_action(mgmt->frame_control) || |
2286 | mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) | 2408 | mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) |
2287 | break; | 2409 | break; |
@@ -2293,105 +2415,60 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, | |||
2293 | break; | 2415 | break; |
2294 | case NL80211_IFTYPE_STATION: | 2416 | case NL80211_IFTYPE_STATION: |
2295 | case NL80211_IFTYPE_P2P_CLIENT: | 2417 | case NL80211_IFTYPE_P2P_CLIENT: |
2418 | if (!sdata->u.mgd.associated) | ||
2419 | need_offchan = true; | ||
2296 | break; | 2420 | break; |
2297 | default: | 2421 | default: |
2298 | return -EOPNOTSUPP; | 2422 | return -EOPNOTSUPP; |
2299 | } | 2423 | } |
2300 | 2424 | ||
2425 | mutex_lock(&local->mtx); | ||
2426 | |||
2427 | /* Check if the operating channel is the requested channel */ | ||
2428 | if (!need_offchan) { | ||
2429 | need_offchan = chan != local->oper_channel; | ||
2430 | if (channel_type_valid && | ||
2431 | channel_type != local->_oper_channel_type) | ||
2432 | need_offchan = true; | ||
2433 | } | ||
2434 | |||
2435 | if (need_offchan && !offchan) { | ||
2436 | ret = -EBUSY; | ||
2437 | goto out_unlock; | ||
2438 | } | ||
2439 | |||
2301 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); | 2440 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); |
2302 | if (!skb) | 2441 | if (!skb) { |
2303 | return -ENOMEM; | 2442 | ret = -ENOMEM; |
2443 | goto out_unlock; | ||
2444 | } | ||
2304 | skb_reserve(skb, local->hw.extra_tx_headroom); | 2445 | skb_reserve(skb, local->hw.extra_tx_headroom); |
2305 | 2446 | ||
2306 | memcpy(skb_put(skb, len), buf, len); | 2447 | memcpy(skb_put(skb, len), buf, len); |
2307 | 2448 | ||
2308 | IEEE80211_SKB_CB(skb)->flags = flags; | 2449 | IEEE80211_SKB_CB(skb)->flags = flags; |
2309 | 2450 | ||
2310 | if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL && | ||
2311 | flags & IEEE80211_TX_CTL_TX_OFFCHAN) | ||
2312 | IEEE80211_SKB_CB(skb)->hw_queue = | ||
2313 | local->hw.offchannel_tx_hw_queue; | ||
2314 | |||
2315 | skb->dev = sdata->dev; | 2451 | skb->dev = sdata->dev; |
2316 | 2452 | ||
2317 | *cookie = (unsigned long) skb; | 2453 | if (!need_offchan) { |
2318 | |||
2319 | if (is_offchan && local->ops->remain_on_channel) { | ||
2320 | unsigned int duration; | ||
2321 | int ret; | ||
2322 | |||
2323 | mutex_lock(&local->mtx); | ||
2324 | /* | ||
2325 | * If the duration is zero, then the driver | ||
2326 | * wouldn't actually do anything. Set it to | ||
2327 | * 100 for now. | ||
2328 | * | ||
2329 | * TODO: cancel the off-channel operation | ||
2330 | * when we get the SKB's TX status and | ||
2331 | * the wait time was zero before. | ||
2332 | */ | ||
2333 | duration = 100; | ||
2334 | if (wait) | ||
2335 | duration = wait; | ||
2336 | ret = ieee80211_remain_on_channel_hw(local, dev, chan, | ||
2337 | channel_type, | ||
2338 | duration, cookie); | ||
2339 | if (ret) { | ||
2340 | kfree_skb(skb); | ||
2341 | mutex_unlock(&local->mtx); | ||
2342 | return ret; | ||
2343 | } | ||
2344 | |||
2345 | local->hw_roc_for_tx = true; | ||
2346 | local->hw_roc_duration = wait; | ||
2347 | |||
2348 | /* | ||
2349 | * queue up frame for transmission after | ||
2350 | * ieee80211_ready_on_channel call | ||
2351 | */ | ||
2352 | |||
2353 | /* modify cookie to prevent API mismatches */ | ||
2354 | *cookie ^= 2; | ||
2355 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN; | ||
2356 | if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) | ||
2357 | IEEE80211_SKB_CB(skb)->hw_queue = | ||
2358 | local->hw.offchannel_tx_hw_queue; | ||
2359 | local->hw_roc_skb = skb; | ||
2360 | local->hw_roc_skb_for_status = skb; | ||
2361 | mutex_unlock(&local->mtx); | ||
2362 | |||
2363 | return 0; | ||
2364 | } | ||
2365 | |||
2366 | /* | ||
2367 | * Can transmit right away if the channel was the | ||
2368 | * right one and there's no wait involved... If a | ||
2369 | * wait is involved, we might otherwise not be on | ||
2370 | * the right channel for long enough! | ||
2371 | */ | ||
2372 | if (!is_offchan && !wait && (in_hw_roc || !sdata->vif.bss_conf.idle)) { | ||
2373 | ieee80211_tx_skb(sdata, skb); | 2454 | ieee80211_tx_skb(sdata, skb); |
2374 | return 0; | 2455 | ret = 0; |
2375 | } | 2456 | goto out_unlock; |
2376 | |||
2377 | wk = kzalloc(sizeof(*wk) + len, GFP_KERNEL); | ||
2378 | if (!wk) { | ||
2379 | kfree_skb(skb); | ||
2380 | return -ENOMEM; | ||
2381 | } | 2457 | } |
2382 | 2458 | ||
2383 | wk->type = IEEE80211_WORK_OFFCHANNEL_TX; | 2459 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN; |
2384 | wk->chan = chan; | 2460 | if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) |
2385 | wk->chan_type = channel_type; | 2461 | IEEE80211_SKB_CB(skb)->hw_queue = |
2386 | wk->sdata = sdata; | 2462 | local->hw.offchannel_tx_hw_queue; |
2387 | wk->done = ieee80211_offchan_tx_done; | ||
2388 | wk->offchan_tx.frame = skb; | ||
2389 | wk->offchan_tx.wait = wait; | ||
2390 | wk->data_len = len; | ||
2391 | memcpy(wk->data, buf, len); | ||
2392 | 2463 | ||
2393 | ieee80211_add_work(wk); | 2464 | /* This will handle all kinds of coalescing and immediate TX */ |
2394 | return 0; | 2465 | ret = ieee80211_start_roc_work(local, sdata, chan, channel_type, |
2466 | wait, cookie, skb); | ||
2467 | if (ret) | ||
2468 | kfree_skb(skb); | ||
2469 | out_unlock: | ||
2470 | mutex_unlock(&local->mtx); | ||
2471 | return ret; | ||
2395 | } | 2472 | } |
2396 | 2473 | ||
2397 | static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, | 2474 | static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, |
@@ -2400,45 +2477,8 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, | |||
2400 | { | 2477 | { |
2401 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 2478 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
2402 | struct ieee80211_local *local = sdata->local; | 2479 | struct ieee80211_local *local = sdata->local; |
2403 | struct ieee80211_work *wk; | ||
2404 | int ret = -ENOENT; | ||
2405 | 2480 | ||
2406 | mutex_lock(&local->mtx); | 2481 | return ieee80211_cancel_roc(local, cookie, true); |
2407 | |||
2408 | if (local->ops->cancel_remain_on_channel) { | ||
2409 | cookie ^= 2; | ||
2410 | ret = ieee80211_cancel_remain_on_channel_hw(local, cookie); | ||
2411 | |||
2412 | if (ret == 0) { | ||
2413 | kfree_skb(local->hw_roc_skb); | ||
2414 | local->hw_roc_skb = NULL; | ||
2415 | local->hw_roc_skb_for_status = NULL; | ||
2416 | } | ||
2417 | |||
2418 | mutex_unlock(&local->mtx); | ||
2419 | |||
2420 | return ret; | ||
2421 | } | ||
2422 | |||
2423 | list_for_each_entry(wk, &local->work_list, list) { | ||
2424 | if (wk->sdata != sdata) | ||
2425 | continue; | ||
2426 | |||
2427 | if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX) | ||
2428 | continue; | ||
2429 | |||
2430 | if (cookie != (unsigned long) wk->offchan_tx.frame) | ||
2431 | continue; | ||
2432 | |||
2433 | wk->timeout = jiffies; | ||
2434 | |||
2435 | ieee80211_queue_work(&local->hw, &local->work_work); | ||
2436 | ret = 0; | ||
2437 | break; | ||
2438 | } | ||
2439 | mutex_unlock(&local->mtx); | ||
2440 | |||
2441 | return ret; | ||
2442 | } | 2482 | } |
2443 | 2483 | ||
2444 | static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, | 2484 | static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8c026abcb8d9..e6cbf5b68c89 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -317,55 +317,30 @@ struct mesh_preq_queue { | |||
317 | u8 flags; | 317 | u8 flags; |
318 | }; | 318 | }; |
319 | 319 | ||
320 | enum ieee80211_work_type { | 320 | #if HZ/100 == 0 |
321 | IEEE80211_WORK_ABORT, | 321 | #define IEEE80211_ROC_MIN_LEFT 1 |
322 | IEEE80211_WORK_REMAIN_ON_CHANNEL, | 322 | #else |
323 | IEEE80211_WORK_OFFCHANNEL_TX, | 323 | #define IEEE80211_ROC_MIN_LEFT (HZ/100) |
324 | }; | 324 | #endif |
325 | |||
326 | /** | ||
327 | * enum work_done_result - indicates what to do after work was done | ||
328 | * | ||
329 | * @WORK_DONE_DESTROY: This work item is no longer needed, destroy. | ||
330 | * @WORK_DONE_REQUEUE: This work item was reset to be reused, and | ||
331 | * should be requeued. | ||
332 | */ | ||
333 | enum work_done_result { | ||
334 | WORK_DONE_DESTROY, | ||
335 | WORK_DONE_REQUEUE, | ||
336 | }; | ||
337 | 325 | ||
338 | struct ieee80211_work { | 326 | struct ieee80211_roc_work { |
339 | struct list_head list; | 327 | struct list_head list; |
328 | struct list_head dependents; | ||
340 | 329 | ||
341 | struct rcu_head rcu_head; | 330 | struct delayed_work work; |
342 | 331 | ||
343 | struct ieee80211_sub_if_data *sdata; | 332 | struct ieee80211_sub_if_data *sdata; |
344 | 333 | ||
345 | enum work_done_result (*done)(struct ieee80211_work *wk, | ||
346 | struct sk_buff *skb); | ||
347 | |||
348 | struct ieee80211_channel *chan; | 334 | struct ieee80211_channel *chan; |
349 | enum nl80211_channel_type chan_type; | 335 | enum nl80211_channel_type chan_type; |
350 | 336 | ||
351 | unsigned long timeout; | 337 | bool started, abort, hw_begun, notified; |
352 | enum ieee80211_work_type type; | ||
353 | 338 | ||
354 | bool started; | 339 | unsigned long hw_start_time; |
355 | 340 | ||
356 | union { | 341 | u32 duration, req_duration; |
357 | struct { | 342 | struct sk_buff *frame; |
358 | u32 duration; | 343 | u64 mgmt_tx_cookie; |
359 | } remain; | ||
360 | struct { | ||
361 | struct sk_buff *frame; | ||
362 | u32 wait; | ||
363 | bool status; | ||
364 | } offchan_tx; | ||
365 | }; | ||
366 | |||
367 | size_t data_len; | ||
368 | u8 data[]; | ||
369 | }; | 344 | }; |
370 | 345 | ||
371 | /* flags used in struct ieee80211_if_managed.flags */ | 346 | /* flags used in struct ieee80211_if_managed.flags */ |
@@ -848,13 +823,6 @@ struct ieee80211_local { | |||
848 | const struct ieee80211_ops *ops; | 823 | const struct ieee80211_ops *ops; |
849 | 824 | ||
850 | /* | 825 | /* |
851 | * work stuff, potentially off-channel (in the future) | ||
852 | */ | ||
853 | struct list_head work_list; | ||
854 | struct timer_list work_timer; | ||
855 | struct work_struct work_work; | ||
856 | |||
857 | /* | ||
858 | * private workqueue to mac80211. mac80211 makes this accessible | 826 | * private workqueue to mac80211. mac80211 makes this accessible |
859 | * via ieee80211_queue_work() | 827 | * via ieee80211_queue_work() |
860 | */ | 828 | */ |
@@ -1088,14 +1056,12 @@ struct ieee80211_local { | |||
1088 | } debugfs; | 1056 | } debugfs; |
1089 | #endif | 1057 | #endif |
1090 | 1058 | ||
1091 | struct ieee80211_channel *hw_roc_channel; | 1059 | /* |
1092 | struct net_device *hw_roc_dev; | 1060 | * Remain-on-channel support |
1093 | struct sk_buff *hw_roc_skb, *hw_roc_skb_for_status; | 1061 | */ |
1062 | struct list_head roc_list; | ||
1094 | struct work_struct hw_roc_start, hw_roc_done; | 1063 | struct work_struct hw_roc_start, hw_roc_done; |
1095 | enum nl80211_channel_type hw_roc_channel_type; | 1064 | unsigned long hw_roc_start_time; |
1096 | unsigned int hw_roc_duration; | ||
1097 | u32 hw_roc_cookie; | ||
1098 | bool hw_roc_for_tx; | ||
1099 | 1065 | ||
1100 | struct idr ack_status_frames; | 1066 | struct idr ack_status_frames; |
1101 | spinlock_t ack_status_lock; | 1067 | spinlock_t ack_status_lock; |
@@ -1291,7 +1257,12 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, | |||
1291 | bool offchannel_ps_enable); | 1257 | bool offchannel_ps_enable); |
1292 | void ieee80211_offchannel_return(struct ieee80211_local *local, | 1258 | void ieee80211_offchannel_return(struct ieee80211_local *local, |
1293 | bool offchannel_ps_disable); | 1259 | bool offchannel_ps_disable); |
1294 | void ieee80211_hw_roc_setup(struct ieee80211_local *local); | 1260 | void ieee80211_roc_setup(struct ieee80211_local *local); |
1261 | void ieee80211_start_next_roc(struct ieee80211_local *local); | ||
1262 | void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata); | ||
1263 | void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc); | ||
1264 | void ieee80211_sw_roc_work(struct work_struct *work); | ||
1265 | void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); | ||
1295 | 1266 | ||
1296 | /* interface handling */ | 1267 | /* interface handling */ |
1297 | int ieee80211_iface_init(void); | 1268 | int ieee80211_iface_init(void); |
@@ -1501,18 +1472,6 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, | |||
1501 | enum nl80211_channel_type channel_type, | 1472 | enum nl80211_channel_type channel_type, |
1502 | u16 prot_mode); | 1473 | u16 prot_mode); |
1503 | 1474 | ||
1504 | /* internal work items */ | ||
1505 | void ieee80211_work_init(struct ieee80211_local *local); | ||
1506 | void ieee80211_add_work(struct ieee80211_work *wk); | ||
1507 | void free_work(struct ieee80211_work *wk); | ||
1508 | void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata); | ||
1509 | int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, | ||
1510 | struct ieee80211_channel *chan, | ||
1511 | enum nl80211_channel_type channel_type, | ||
1512 | unsigned int duration, u64 *cookie); | ||
1513 | int ieee80211_wk_cancel_remain_on_channel( | ||
1514 | struct ieee80211_sub_if_data *sdata, u64 cookie); | ||
1515 | |||
1516 | /* channel management */ | 1475 | /* channel management */ |
1517 | enum ieee80211_chan_mode { | 1476 | enum ieee80211_chan_mode { |
1518 | CHAN_MODE_UNDEFINED, | 1477 | CHAN_MODE_UNDEFINED, |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 968d71c50713..87aeb4f21ffd 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -528,10 +528,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
528 | */ | 528 | */ |
529 | netif_tx_stop_all_queues(sdata->dev); | 529 | netif_tx_stop_all_queues(sdata->dev); |
530 | 530 | ||
531 | /* | 531 | ieee80211_roc_purge(sdata); |
532 | * Purge work for this interface. | ||
533 | */ | ||
534 | ieee80211_work_purge(sdata); | ||
535 | 532 | ||
536 | /* | 533 | /* |
537 | * Remove all stations associated with this interface. | 534 | * Remove all stations associated with this interface. |
@@ -637,18 +634,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
637 | ieee80211_configure_filter(local); | 634 | ieee80211_configure_filter(local); |
638 | break; | 635 | break; |
639 | default: | 636 | default: |
640 | mutex_lock(&local->mtx); | ||
641 | if (local->hw_roc_dev == sdata->dev && | ||
642 | local->hw_roc_channel) { | ||
643 | /* ignore return value since this is racy */ | ||
644 | drv_cancel_remain_on_channel(local); | ||
645 | ieee80211_queue_work(&local->hw, &local->hw_roc_done); | ||
646 | } | ||
647 | mutex_unlock(&local->mtx); | ||
648 | |||
649 | flush_work(&local->hw_roc_start); | ||
650 | flush_work(&local->hw_roc_done); | ||
651 | |||
652 | flush_work(&sdata->work); | 637 | flush_work(&sdata->work); |
653 | /* | 638 | /* |
654 | * When we get here, the interface is marked down. | 639 | * When we get here, the interface is marked down. |
@@ -1457,8 +1442,8 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) | |||
1457 | struct ieee80211_sub_if_data *sdata; | 1442 | struct ieee80211_sub_if_data *sdata; |
1458 | int count = 0; | 1443 | int count = 0; |
1459 | bool working = false, scanning = false; | 1444 | bool working = false, scanning = false; |
1460 | struct ieee80211_work *wk; | ||
1461 | unsigned int led_trig_start = 0, led_trig_stop = 0; | 1445 | unsigned int led_trig_start = 0, led_trig_stop = 0; |
1446 | struct ieee80211_roc_work *roc; | ||
1462 | 1447 | ||
1463 | #ifdef CONFIG_PROVE_LOCKING | 1448 | #ifdef CONFIG_PROVE_LOCKING |
1464 | WARN_ON(debug_locks && !lockdep_rtnl_is_held() && | 1449 | WARN_ON(debug_locks && !lockdep_rtnl_is_held() && |
@@ -1494,9 +1479,9 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) | |||
1494 | } | 1479 | } |
1495 | 1480 | ||
1496 | if (!local->ops->remain_on_channel) { | 1481 | if (!local->ops->remain_on_channel) { |
1497 | list_for_each_entry(wk, &local->work_list, list) { | 1482 | list_for_each_entry(roc, &local->roc_list, list) { |
1498 | working = true; | 1483 | working = true; |
1499 | wk->sdata->vif.bss_conf.idle = false; | 1484 | roc->sdata->vif.bss_conf.idle = false; |
1500 | } | 1485 | } |
1501 | } | 1486 | } |
1502 | 1487 | ||
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 779ac613ee57..d81c178c7712 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -625,8 +625,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
625 | 625 | ||
626 | INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); | 626 | INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); |
627 | 627 | ||
628 | ieee80211_work_init(local); | ||
629 | |||
630 | INIT_WORK(&local->restart_work, ieee80211_restart_work); | 628 | INIT_WORK(&local->restart_work, ieee80211_restart_work); |
631 | 629 | ||
632 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); | 630 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); |
@@ -669,7 +667,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
669 | 667 | ||
670 | ieee80211_led_names(local); | 668 | ieee80211_led_names(local); |
671 | 669 | ||
672 | ieee80211_hw_roc_setup(local); | 670 | ieee80211_roc_setup(local); |
673 | 671 | ||
674 | return &local->hw; | 672 | return &local->hw; |
675 | } | 673 | } |
@@ -1016,12 +1014,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) | |||
1016 | 1014 | ||
1017 | rtnl_unlock(); | 1015 | rtnl_unlock(); |
1018 | 1016 | ||
1019 | /* | ||
1020 | * Now all work items will be gone, but the | ||
1021 | * timer might still be armed, so delete it | ||
1022 | */ | ||
1023 | del_timer_sync(&local->work_timer); | ||
1024 | |||
1025 | cancel_work_sync(&local->restart_work); | 1017 | cancel_work_sync(&local->restart_work); |
1026 | cancel_work_sync(&local->reconfig_filter); | 1018 | cancel_work_sync(&local->reconfig_filter); |
1027 | 1019 | ||
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 8f482b15bc51..abb226dc4753 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <net/mac80211.h> | 16 | #include <net/mac80211.h> |
17 | #include "ieee80211_i.h" | 17 | #include "ieee80211_i.h" |
18 | #include "driver-trace.h" | 18 | #include "driver-trace.h" |
19 | #include "driver-ops.h" | ||
19 | 20 | ||
20 | /* | 21 | /* |
21 | * Tell our hardware to disable PS. | 22 | * Tell our hardware to disable PS. |
@@ -181,32 +182,58 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, | |||
181 | mutex_unlock(&local->iflist_mtx); | 182 | mutex_unlock(&local->iflist_mtx); |
182 | } | 183 | } |
183 | 184 | ||
185 | void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc) | ||
186 | { | ||
187 | if (roc->notified) | ||
188 | return; | ||
189 | |||
190 | if (roc->mgmt_tx_cookie) { | ||
191 | if (!WARN_ON(!roc->frame)) { | ||
192 | ieee80211_tx_skb(roc->sdata, roc->frame); | ||
193 | roc->frame = NULL; | ||
194 | } | ||
195 | } else { | ||
196 | cfg80211_ready_on_channel(roc->sdata->dev, (unsigned long)roc, | ||
197 | roc->chan, roc->chan_type, | ||
198 | roc->req_duration, GFP_KERNEL); | ||
199 | } | ||
200 | |||
201 | roc->notified = true; | ||
202 | } | ||
203 | |||
184 | static void ieee80211_hw_roc_start(struct work_struct *work) | 204 | static void ieee80211_hw_roc_start(struct work_struct *work) |
185 | { | 205 | { |
186 | struct ieee80211_local *local = | 206 | struct ieee80211_local *local = |
187 | container_of(work, struct ieee80211_local, hw_roc_start); | 207 | container_of(work, struct ieee80211_local, hw_roc_start); |
188 | struct ieee80211_sub_if_data *sdata; | 208 | struct ieee80211_roc_work *roc, *dep, *tmp; |
189 | 209 | ||
190 | mutex_lock(&local->mtx); | 210 | mutex_lock(&local->mtx); |
191 | 211 | ||
192 | if (!local->hw_roc_channel) { | 212 | if (list_empty(&local->roc_list)) |
193 | mutex_unlock(&local->mtx); | 213 | goto out_unlock; |
194 | return; | ||
195 | } | ||
196 | 214 | ||
197 | if (local->hw_roc_skb) { | 215 | roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, |
198 | sdata = IEEE80211_DEV_TO_SUB_IF(local->hw_roc_dev); | 216 | list); |
199 | ieee80211_tx_skb(sdata, local->hw_roc_skb); | 217 | |
200 | local->hw_roc_skb = NULL; | 218 | if (!roc->started) |
201 | } else { | 219 | goto out_unlock; |
202 | cfg80211_ready_on_channel(local->hw_roc_dev, | 220 | |
203 | local->hw_roc_cookie, | 221 | roc->hw_begun = true; |
204 | local->hw_roc_channel, | 222 | roc->hw_start_time = local->hw_roc_start_time; |
205 | local->hw_roc_channel_type, | ||
206 | local->hw_roc_duration, | ||
207 | GFP_KERNEL); | ||
208 | } | ||
209 | 223 | ||
224 | ieee80211_handle_roc_started(roc); | ||
225 | list_for_each_entry_safe(dep, tmp, &roc->dependents, list) { | ||
226 | ieee80211_handle_roc_started(dep); | ||
227 | |||
228 | if (dep->duration > roc->duration) { | ||
229 | u32 dur = dep->duration; | ||
230 | dep->duration = dur - roc->duration; | ||
231 | roc->duration = dur; | ||
232 | list_del(&dep->list); | ||
233 | list_add(&dep->list, &roc->list); | ||
234 | } | ||
235 | } | ||
236 | out_unlock: | ||
210 | mutex_unlock(&local->mtx); | 237 | mutex_unlock(&local->mtx); |
211 | } | 238 | } |
212 | 239 | ||
@@ -214,50 +241,179 @@ void ieee80211_ready_on_channel(struct ieee80211_hw *hw) | |||
214 | { | 241 | { |
215 | struct ieee80211_local *local = hw_to_local(hw); | 242 | struct ieee80211_local *local = hw_to_local(hw); |
216 | 243 | ||
244 | local->hw_roc_start_time = jiffies; | ||
245 | |||
217 | trace_api_ready_on_channel(local); | 246 | trace_api_ready_on_channel(local); |
218 | 247 | ||
219 | ieee80211_queue_work(hw, &local->hw_roc_start); | 248 | ieee80211_queue_work(hw, &local->hw_roc_start); |
220 | } | 249 | } |
221 | EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel); | 250 | EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel); |
222 | 251 | ||
223 | static void ieee80211_hw_roc_done(struct work_struct *work) | 252 | void ieee80211_start_next_roc(struct ieee80211_local *local) |
224 | { | 253 | { |
225 | struct ieee80211_local *local = | 254 | struct ieee80211_roc_work *roc; |
226 | container_of(work, struct ieee80211_local, hw_roc_done); | ||
227 | 255 | ||
228 | mutex_lock(&local->mtx); | 256 | lockdep_assert_held(&local->mtx); |
229 | 257 | ||
230 | if (!local->hw_roc_channel) { | 258 | if (list_empty(&local->roc_list)) { |
231 | mutex_unlock(&local->mtx); | 259 | ieee80211_run_deferred_scan(local); |
232 | return; | 260 | return; |
233 | } | 261 | } |
234 | 262 | ||
235 | /* was never transmitted */ | 263 | roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, |
236 | if (local->hw_roc_skb) { | 264 | list); |
237 | u64 cookie; | ||
238 | 265 | ||
239 | cookie = local->hw_roc_cookie ^ 2; | 266 | if (local->ops->remain_on_channel) { |
267 | int ret, duration = roc->duration; | ||
240 | 268 | ||
241 | cfg80211_mgmt_tx_status(local->hw_roc_dev, cookie, | 269 | /* XXX: duplicated, see ieee80211_start_roc_work() */ |
242 | local->hw_roc_skb->data, | 270 | if (!duration) |
243 | local->hw_roc_skb->len, false, | 271 | duration = 10; |
244 | GFP_KERNEL); | ||
245 | 272 | ||
246 | kfree_skb(local->hw_roc_skb); | 273 | ret = drv_remain_on_channel(local, roc->chan, |
247 | local->hw_roc_skb = NULL; | 274 | roc->chan_type, |
248 | local->hw_roc_skb_for_status = NULL; | 275 | duration); |
276 | |||
277 | roc->started = true; | ||
278 | |||
279 | if (ret) { | ||
280 | wiphy_warn(local->hw.wiphy, | ||
281 | "failed to start next HW ROC (%d)\n", ret); | ||
282 | /* | ||
283 | * queue the work struct again to avoid recursion | ||
284 | * when multiple failures occur | ||
285 | */ | ||
286 | ieee80211_remain_on_channel_expired(&local->hw); | ||
287 | } | ||
288 | } else { | ||
289 | /* delay it a bit */ | ||
290 | ieee80211_queue_delayed_work(&local->hw, &roc->work, | ||
291 | round_jiffies_relative(HZ/2)); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) | ||
296 | { | ||
297 | struct ieee80211_roc_work *dep, *tmp; | ||
298 | |||
299 | /* was never transmitted */ | ||
300 | if (roc->frame) { | ||
301 | cfg80211_mgmt_tx_status(roc->sdata->dev, | ||
302 | (unsigned long)roc->frame, | ||
303 | roc->frame->data, roc->frame->len, | ||
304 | false, GFP_KERNEL); | ||
305 | kfree_skb(roc->frame); | ||
249 | } | 306 | } |
250 | 307 | ||
251 | if (!local->hw_roc_for_tx) | 308 | if (!roc->mgmt_tx_cookie) |
252 | cfg80211_remain_on_channel_expired(local->hw_roc_dev, | 309 | cfg80211_remain_on_channel_expired(roc->sdata->dev, |
253 | local->hw_roc_cookie, | 310 | (unsigned long)roc, |
254 | local->hw_roc_channel, | 311 | roc->chan, roc->chan_type, |
255 | local->hw_roc_channel_type, | ||
256 | GFP_KERNEL); | 312 | GFP_KERNEL); |
257 | 313 | ||
258 | local->hw_roc_channel = NULL; | 314 | list_for_each_entry_safe(dep, tmp, &roc->dependents, list) |
259 | local->hw_roc_cookie = 0; | 315 | ieee80211_roc_notify_destroy(dep); |
316 | |||
317 | kfree(roc); | ||
318 | } | ||
319 | |||
320 | void ieee80211_sw_roc_work(struct work_struct *work) | ||
321 | { | ||
322 | struct ieee80211_roc_work *roc = | ||
323 | container_of(work, struct ieee80211_roc_work, work.work); | ||
324 | struct ieee80211_sub_if_data *sdata = roc->sdata; | ||
325 | struct ieee80211_local *local = sdata->local; | ||
326 | |||
327 | mutex_lock(&local->mtx); | ||
328 | |||
329 | if (roc->abort) | ||
330 | goto finish; | ||
331 | |||
332 | if (WARN_ON(list_empty(&local->roc_list))) | ||
333 | goto out_unlock; | ||
334 | |||
335 | if (WARN_ON(roc != list_first_entry(&local->roc_list, | ||
336 | struct ieee80211_roc_work, | ||
337 | list))) | ||
338 | goto out_unlock; | ||
339 | |||
340 | if (!roc->started) { | ||
341 | struct ieee80211_roc_work *dep; | ||
342 | |||
343 | /* start this ROC */ | ||
260 | 344 | ||
345 | /* switch channel etc */ | ||
346 | ieee80211_recalc_idle(local); | ||
347 | |||
348 | local->tmp_channel = roc->chan; | ||
349 | local->tmp_channel_type = roc->chan_type; | ||
350 | ieee80211_hw_config(local, 0); | ||
351 | |||
352 | /* tell userspace or send frame */ | ||
353 | ieee80211_handle_roc_started(roc); | ||
354 | list_for_each_entry(dep, &roc->dependents, list) | ||
355 | ieee80211_handle_roc_started(dep); | ||
356 | |||
357 | /* if it was pure TX, just finish right away */ | ||
358 | if (!roc->duration) | ||
359 | goto finish; | ||
360 | |||
361 | roc->started = true; | ||
362 | ieee80211_queue_delayed_work(&local->hw, &roc->work, | ||
363 | msecs_to_jiffies(roc->duration)); | ||
364 | } else { | ||
365 | /* finish this ROC */ | ||
366 | finish: | ||
367 | list_del(&roc->list); | ||
368 | ieee80211_roc_notify_destroy(roc); | ||
369 | |||
370 | if (roc->started) { | ||
371 | drv_flush(local, false); | ||
372 | |||
373 | local->tmp_channel = NULL; | ||
374 | ieee80211_hw_config(local, 0); | ||
375 | |||
376 | ieee80211_offchannel_return(local, true); | ||
377 | } | ||
378 | |||
379 | ieee80211_recalc_idle(local); | ||
380 | |||
381 | ieee80211_start_next_roc(local); | ||
382 | ieee80211_run_deferred_scan(local); | ||
383 | } | ||
384 | |||
385 | out_unlock: | ||
386 | mutex_unlock(&local->mtx); | ||
387 | } | ||
388 | |||
389 | static void ieee80211_hw_roc_done(struct work_struct *work) | ||
390 | { | ||
391 | struct ieee80211_local *local = | ||
392 | container_of(work, struct ieee80211_local, hw_roc_done); | ||
393 | struct ieee80211_roc_work *roc; | ||
394 | |||
395 | mutex_lock(&local->mtx); | ||
396 | |||
397 | if (list_empty(&local->roc_list)) | ||
398 | goto out_unlock; | ||
399 | |||
400 | roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, | ||
401 | list); | ||
402 | |||
403 | if (!roc->started) | ||
404 | goto out_unlock; | ||
405 | |||
406 | list_del(&roc->list); | ||
407 | |||
408 | ieee80211_roc_notify_destroy(roc); | ||
409 | |||
410 | /* if there's another roc, start it now */ | ||
411 | ieee80211_start_next_roc(local); | ||
412 | |||
413 | /* or scan maybe */ | ||
414 | ieee80211_run_deferred_scan(local); | ||
415 | |||
416 | out_unlock: | ||
261 | mutex_unlock(&local->mtx); | 417 | mutex_unlock(&local->mtx); |
262 | } | 418 | } |
263 | 419 | ||
@@ -271,8 +427,48 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw) | |||
271 | } | 427 | } |
272 | EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired); | 428 | EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired); |
273 | 429 | ||
274 | void ieee80211_hw_roc_setup(struct ieee80211_local *local) | 430 | void ieee80211_roc_setup(struct ieee80211_local *local) |
275 | { | 431 | { |
276 | INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start); | 432 | INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start); |
277 | INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done); | 433 | INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done); |
434 | INIT_LIST_HEAD(&local->roc_list); | ||
435 | } | ||
436 | |||
437 | void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata) | ||
438 | { | ||
439 | struct ieee80211_local *local = sdata->local; | ||
440 | struct ieee80211_roc_work *roc, *tmp; | ||
441 | LIST_HEAD(tmp_list); | ||
442 | |||
443 | mutex_lock(&local->mtx); | ||
444 | list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { | ||
445 | if (roc->sdata != sdata) | ||
446 | continue; | ||
447 | |||
448 | if (roc->started && local->ops->remain_on_channel) { | ||
449 | /* can race, so ignore return value */ | ||
450 | drv_cancel_remain_on_channel(local); | ||
451 | } | ||
452 | |||
453 | list_move_tail(&roc->list, &tmp_list); | ||
454 | roc->abort = true; | ||
455 | } | ||
456 | |||
457 | ieee80211_start_next_roc(local); | ||
458 | ieee80211_run_deferred_scan(local); | ||
459 | mutex_unlock(&local->mtx); | ||
460 | |||
461 | list_for_each_entry_safe(roc, tmp, &tmp_list, list) { | ||
462 | if (local->ops->remain_on_channel) { | ||
463 | list_del(&roc->list); | ||
464 | ieee80211_roc_notify_destroy(roc); | ||
465 | } else { | ||
466 | ieee80211_queue_delayed_work(&local->hw, &roc->work, 0); | ||
467 | |||
468 | /* work will clean up etc */ | ||
469 | flush_delayed_work(&roc->work); | ||
470 | } | ||
471 | } | ||
472 | |||
473 | WARN_ON_ONCE(!list_empty(&tmp_list)); | ||
278 | } | 474 | } |
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 169da0742c81..379f178eab5f 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -323,7 +323,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, | |||
323 | ieee80211_mlme_notify_scan_completed(local); | 323 | ieee80211_mlme_notify_scan_completed(local); |
324 | ieee80211_ibss_notify_scan_completed(local); | 324 | ieee80211_ibss_notify_scan_completed(local); |
325 | ieee80211_mesh_notify_scan_completed(local); | 325 | ieee80211_mesh_notify_scan_completed(local); |
326 | ieee80211_queue_work(&local->hw, &local->work_work); | 326 | ieee80211_start_next_roc(local); |
327 | } | 327 | } |
328 | 328 | ||
329 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | 329 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) |
@@ -376,7 +376,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
376 | static bool ieee80211_can_scan(struct ieee80211_local *local, | 376 | static bool ieee80211_can_scan(struct ieee80211_local *local, |
377 | struct ieee80211_sub_if_data *sdata) | 377 | struct ieee80211_sub_if_data *sdata) |
378 | { | 378 | { |
379 | if (!list_empty(&local->work_list)) | 379 | if (!list_empty(&local->roc_list)) |
380 | return false; | 380 | return false; |
381 | 381 | ||
382 | if (sdata->vif.type == NL80211_IFTYPE_STATION && | 382 | if (sdata->vif.type == NL80211_IFTYPE_STATION && |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 63a769015068..6b4f42527887 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -520,36 +520,16 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
520 | 520 | ||
521 | if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { | 521 | if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { |
522 | u64 cookie = (unsigned long)skb; | 522 | u64 cookie = (unsigned long)skb; |
523 | acked = info->flags & IEEE80211_TX_STAT_ACK; | ||
523 | 524 | ||
524 | if (ieee80211_is_nullfunc(hdr->frame_control) || | 525 | if (ieee80211_is_nullfunc(hdr->frame_control) || |
525 | ieee80211_is_qos_nullfunc(hdr->frame_control)) { | 526 | ieee80211_is_qos_nullfunc(hdr->frame_control)) |
526 | acked = info->flags & IEEE80211_TX_STAT_ACK; | ||
527 | |||
528 | cfg80211_probe_status(skb->dev, hdr->addr1, | 527 | cfg80211_probe_status(skb->dev, hdr->addr1, |
529 | cookie, acked, GFP_ATOMIC); | 528 | cookie, acked, GFP_ATOMIC); |
530 | } else { | 529 | else |
531 | struct ieee80211_work *wk; | ||
532 | |||
533 | rcu_read_lock(); | ||
534 | list_for_each_entry_rcu(wk, &local->work_list, list) { | ||
535 | if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX) | ||
536 | continue; | ||
537 | if (wk->offchan_tx.frame != skb) | ||
538 | continue; | ||
539 | wk->offchan_tx.status = true; | ||
540 | break; | ||
541 | } | ||
542 | rcu_read_unlock(); | ||
543 | if (local->hw_roc_skb_for_status == skb) { | ||
544 | cookie = local->hw_roc_cookie ^ 2; | ||
545 | local->hw_roc_skb_for_status = NULL; | ||
546 | } | ||
547 | |||
548 | cfg80211_mgmt_tx_status( | 530 | cfg80211_mgmt_tx_status( |
549 | skb->dev, cookie, skb->data, skb->len, | 531 | skb->dev, cookie, skb->data, skb->len, |
550 | !!(info->flags & IEEE80211_TX_STAT_ACK), | 532 | acked, GFP_ATOMIC); |
551 | GFP_ATOMIC); | ||
552 | } | ||
553 | } | 533 | } |
554 | 534 | ||
555 | if (unlikely(info->ack_frame_id)) { | 535 | if (unlikely(info->ack_frame_id)) { |
diff --git a/net/mac80211/work.c b/net/mac80211/work.c deleted file mode 100644 index b2650a9d45ff..000000000000 --- a/net/mac80211/work.c +++ /dev/null | |||
@@ -1,370 +0,0 @@ | |||
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> | ||
22 | #include <linux/slab.h> | ||
23 | #include <net/mac80211.h> | ||
24 | #include <asm/unaligned.h> | ||
25 | |||
26 | #include "ieee80211_i.h" | ||
27 | #include "rate.h" | ||
28 | #include "driver-ops.h" | ||
29 | |||
30 | enum work_action { | ||
31 | WORK_ACT_NONE, | ||
32 | WORK_ACT_TIMEOUT, | ||
33 | }; | ||
34 | |||
35 | |||
36 | /* utils */ | ||
37 | static inline void ASSERT_WORK_MTX(struct ieee80211_local *local) | ||
38 | { | ||
39 | lockdep_assert_held(&local->mtx); | ||
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 | |||
62 | void free_work(struct ieee80211_work *wk) | ||
63 | { | ||
64 | kfree_rcu(wk, rcu_head); | ||
65 | } | ||
66 | |||
67 | static enum work_action __must_check | ||
68 | ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk) | ||
69 | { | ||
70 | /* | ||
71 | * First time we run, do nothing -- the generic code will | ||
72 | * have switched to the right channel etc. | ||
73 | */ | ||
74 | if (!wk->started) { | ||
75 | wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration); | ||
76 | |||
77 | cfg80211_ready_on_channel(wk->sdata->dev, (unsigned long) wk, | ||
78 | wk->chan, wk->chan_type, | ||
79 | wk->remain.duration, GFP_KERNEL); | ||
80 | |||
81 | return WORK_ACT_NONE; | ||
82 | } | ||
83 | |||
84 | return WORK_ACT_TIMEOUT; | ||
85 | } | ||
86 | |||
87 | static enum work_action __must_check | ||
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 | ||
96 | * cookie for canceling this work/status matching. | ||
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 | |||
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); | ||
120 | struct ieee80211_work *wk, *tmp; | ||
121 | LIST_HEAD(free_work); | ||
122 | enum work_action rma; | ||
123 | bool remain_off_channel = false; | ||
124 | |||
125 | /* | ||
126 | * ieee80211_queue_work() should have picked up most cases, | ||
127 | * here we'll pick the rest. | ||
128 | */ | ||
129 | if (WARN(local->suspended, "work scheduled while going to suspend\n")) | ||
130 | return; | ||
131 | |||
132 | mutex_lock(&local->mtx); | ||
133 | |||
134 | if (local->scanning) { | ||
135 | mutex_unlock(&local->mtx); | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | ieee80211_recalc_idle(local); | ||
140 | |||
141 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { | ||
142 | bool started = wk->started; | ||
143 | |||
144 | /* mark work as started if it's on the current off-channel */ | ||
145 | if (!started && local->tmp_channel && | ||
146 | wk->chan == local->tmp_channel && | ||
147 | wk->chan_type == local->tmp_channel_type) { | ||
148 | started = true; | ||
149 | wk->timeout = jiffies; | ||
150 | } | ||
151 | |||
152 | if (!started && !local->tmp_channel) { | ||
153 | ieee80211_offchannel_stop_vifs(local, true); | ||
154 | |||
155 | local->tmp_channel = wk->chan; | ||
156 | local->tmp_channel_type = wk->chan_type; | ||
157 | |||
158 | ieee80211_hw_config(local, 0); | ||
159 | |||
160 | started = true; | ||
161 | wk->timeout = jiffies; | ||
162 | } | ||
163 | |||
164 | /* don't try to work with items that aren't started */ | ||
165 | if (!started) | ||
166 | continue; | ||
167 | |||
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; | ||
184 | case IEEE80211_WORK_ABORT: | ||
185 | rma = WORK_ACT_TIMEOUT; | ||
186 | break; | ||
187 | case IEEE80211_WORK_REMAIN_ON_CHANNEL: | ||
188 | rma = ieee80211_remain_on_channel_timeout(wk); | ||
189 | break; | ||
190 | case IEEE80211_WORK_OFFCHANNEL_TX: | ||
191 | rma = ieee80211_offchannel_tx(wk); | ||
192 | break; | ||
193 | } | ||
194 | |||
195 | wk->started = started; | ||
196 | |||
197 | switch (rma) { | ||
198 | case WORK_ACT_NONE: | ||
199 | /* might have changed the timeout */ | ||
200 | run_again(local, wk->timeout); | ||
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 | |||
212 | list_for_each_entry(wk, &local->work_list, list) { | ||
213 | if (!wk->started) | ||
214 | continue; | ||
215 | if (wk->chan != local->tmp_channel || | ||
216 | wk->chan_type != local->tmp_channel_type) | ||
217 | continue; | ||
218 | remain_off_channel = true; | ||
219 | } | ||
220 | |||
221 | if (!remain_off_channel && local->tmp_channel) { | ||
222 | local->tmp_channel = NULL; | ||
223 | ieee80211_hw_config(local, 0); | ||
224 | |||
225 | ieee80211_offchannel_return(local, true); | ||
226 | |||
227 | /* give connection some time to breathe */ | ||
228 | run_again(local, jiffies + HZ/2); | ||
229 | } | ||
230 | |||
231 | ieee80211_recalc_idle(local); | ||
232 | ieee80211_run_deferred_scan(local); | ||
233 | |||
234 | mutex_unlock(&local->mtx); | ||
235 | |||
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 | |||
256 | if (WARN_ON(!ieee80211_sdata_running(wk->sdata))) | ||
257 | return; | ||
258 | |||
259 | wk->started = false; | ||
260 | |||
261 | local = wk->sdata->local; | ||
262 | mutex_lock(&local->mtx); | ||
263 | list_add_tail(&wk->list, &local->work_list); | ||
264 | mutex_unlock(&local->mtx); | ||
265 | |||
266 | ieee80211_queue_work(&local->hw, &local->work_work); | ||
267 | } | ||
268 | |||
269 | void ieee80211_work_init(struct ieee80211_local *local) | ||
270 | { | ||
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); | ||
275 | } | ||
276 | |||
277 | void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) | ||
278 | { | ||
279 | struct ieee80211_local *local = sdata->local; | ||
280 | struct ieee80211_work *wk; | ||
281 | bool cleanup = false; | ||
282 | |||
283 | mutex_lock(&local->mtx); | ||
284 | list_for_each_entry(wk, &local->work_list, list) { | ||
285 | if (wk->sdata != sdata) | ||
286 | continue; | ||
287 | cleanup = true; | ||
288 | wk->type = IEEE80211_WORK_ABORT; | ||
289 | wk->started = true; | ||
290 | wk->timeout = jiffies; | ||
291 | } | ||
292 | mutex_unlock(&local->mtx); | ||
293 | |||
294 | /* run cleanups etc. */ | ||
295 | if (cleanup) | ||
296 | ieee80211_work_work(&local->work_work); | ||
297 | |||
298 | mutex_lock(&local->mtx); | ||
299 | list_for_each_entry(wk, &local->work_list, list) { | ||
300 | if (wk->sdata != sdata) | ||
301 | continue; | ||
302 | WARN_ON(1); | ||
303 | break; | ||
304 | } | ||
305 | mutex_unlock(&local->mtx); | ||
306 | } | ||
307 | |||
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 | */ | ||
314 | cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk, | ||
315 | wk->chan, wk->chan_type, | ||
316 | GFP_KERNEL); | ||
317 | |||
318 | return WORK_DONE_DESTROY; | ||
319 | } | ||
320 | |||
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 | { | ||
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; | ||
334 | wk->chan_type = channel_type; | ||
335 | wk->sdata = sdata; | ||
336 | wk->done = ieee80211_remain_done; | ||
337 | |||
338 | wk->remain.duration = duration; | ||
339 | |||
340 | *cookie = (unsigned long) wk; | ||
341 | |||
342 | ieee80211_add_work(wk); | ||
343 | |||
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 | |||
354 | mutex_lock(&local->mtx); | ||
355 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { | ||
356 | if ((unsigned long) wk == cookie) { | ||
357 | wk->timeout = jiffies; | ||
358 | found = true; | ||
359 | break; | ||
360 | } | ||
361 | } | ||
362 | mutex_unlock(&local->mtx); | ||
363 | |||
364 | if (!found) | ||
365 | return -ENOENT; | ||
366 | |||
367 | ieee80211_queue_work(&local->hw, &local->work_work); | ||
368 | |||
369 | return 0; | ||
370 | } | ||