diff options
-rw-r--r-- | include/net/mac80211.h | 42 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 31 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 43 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 2 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 24 | ||||
-rw-r--r-- | net/mac80211/work.c | 25 |
6 files changed, 164 insertions, 3 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 972109f06a70..9259e97864d7 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -1586,6 +1586,20 @@ enum ieee80211_ampdu_mlme_action { | |||
1586 | }; | 1586 | }; |
1587 | 1587 | ||
1588 | /** | 1588 | /** |
1589 | * enum ieee80211_tx_sync_type - TX sync type | ||
1590 | * @IEEE80211_TX_SYNC_AUTH: sync TX for authentication | ||
1591 | * (and possibly also before direct probe) | ||
1592 | * @IEEE80211_TX_SYNC_ASSOC: sync TX for association | ||
1593 | * @IEEE80211_TX_SYNC_ACTION: sync TX for action frame | ||
1594 | * (not implemented yet) | ||
1595 | */ | ||
1596 | enum ieee80211_tx_sync_type { | ||
1597 | IEEE80211_TX_SYNC_AUTH, | ||
1598 | IEEE80211_TX_SYNC_ASSOC, | ||
1599 | IEEE80211_TX_SYNC_ACTION, | ||
1600 | }; | ||
1601 | |||
1602 | /** | ||
1589 | * struct ieee80211_ops - callbacks from mac80211 to the driver | 1603 | * struct ieee80211_ops - callbacks from mac80211 to the driver |
1590 | * | 1604 | * |
1591 | * This structure contains various callbacks that the driver may | 1605 | * This structure contains various callbacks that the driver may |
@@ -1674,6 +1688,26 @@ enum ieee80211_ampdu_mlme_action { | |||
1674 | * of the bss parameters has changed when a call is made. The callback | 1688 | * of the bss parameters has changed when a call is made. The callback |
1675 | * can sleep. | 1689 | * can sleep. |
1676 | * | 1690 | * |
1691 | * @tx_sync: Called before a frame is sent to an AP/GO. In the GO case, the | ||
1692 | * driver should sync with the GO's powersaving so the device doesn't | ||
1693 | * transmit the frame while the GO is asleep. In the regular AP case | ||
1694 | * it may be used by drivers for devices implementing other restrictions | ||
1695 | * on talking to APs, e.g. due to regulatory enforcement or just HW | ||
1696 | * restrictions. | ||
1697 | * This function is called for every authentication, association and | ||
1698 | * action frame separately since applications might attempt to auth | ||
1699 | * with multiple APs before chosing one to associate to. If it returns | ||
1700 | * an error, the corresponding authentication, association or frame | ||
1701 | * transmission is aborted and reported as having failed. It is always | ||
1702 | * called after tuning to the correct channel. | ||
1703 | * The callback might be called multiple times before @finish_tx_sync | ||
1704 | * (but @finish_tx_sync will be called once for each) but in practice | ||
1705 | * this is unlikely to happen. It can also refuse in that case if the | ||
1706 | * driver cannot handle that situation. | ||
1707 | * This callback can sleep. | ||
1708 | * @finish_tx_sync: Called as a counterpart to @tx_sync, unless that returned | ||
1709 | * an error. This callback can sleep. | ||
1710 | * | ||
1677 | * @prepare_multicast: Prepare for multicast filter configuration. | 1711 | * @prepare_multicast: Prepare for multicast filter configuration. |
1678 | * This callback is optional, and its return value is passed | 1712 | * This callback is optional, and its return value is passed |
1679 | * to configure_filter(). This callback must be atomic. | 1713 | * to configure_filter(). This callback must be atomic. |
@@ -1901,6 +1935,14 @@ struct ieee80211_ops { | |||
1901 | struct ieee80211_vif *vif, | 1935 | struct ieee80211_vif *vif, |
1902 | struct ieee80211_bss_conf *info, | 1936 | struct ieee80211_bss_conf *info, |
1903 | u32 changed); | 1937 | u32 changed); |
1938 | |||
1939 | int (*tx_sync)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | ||
1940 | const u8 *bssid, enum ieee80211_tx_sync_type type); | ||
1941 | void (*finish_tx_sync)(struct ieee80211_hw *hw, | ||
1942 | struct ieee80211_vif *vif, | ||
1943 | const u8 *bssid, | ||
1944 | enum ieee80211_tx_sync_type type); | ||
1945 | |||
1904 | u64 (*prepare_multicast)(struct ieee80211_hw *hw, | 1946 | u64 (*prepare_multicast)(struct ieee80211_hw *hw, |
1905 | struct netdev_hw_addr_list *mc_list); | 1947 | struct netdev_hw_addr_list *mc_list); |
1906 | void (*configure_filter)(struct ieee80211_hw *hw, | 1948 | void (*configure_filter)(struct ieee80211_hw *hw, |
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index b2d6bba44054..1425380983f7 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h | |||
@@ -130,6 +130,37 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, | |||
130 | trace_drv_return_void(local); | 130 | trace_drv_return_void(local); |
131 | } | 131 | } |
132 | 132 | ||
133 | static inline int drv_tx_sync(struct ieee80211_local *local, | ||
134 | struct ieee80211_sub_if_data *sdata, | ||
135 | const u8 *bssid, | ||
136 | enum ieee80211_tx_sync_type type) | ||
137 | { | ||
138 | int ret = 0; | ||
139 | |||
140 | might_sleep(); | ||
141 | |||
142 | trace_drv_tx_sync(local, sdata, bssid, type); | ||
143 | if (local->ops->tx_sync) | ||
144 | ret = local->ops->tx_sync(&local->hw, &sdata->vif, | ||
145 | bssid, type); | ||
146 | trace_drv_return_int(local, ret); | ||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | static inline void drv_finish_tx_sync(struct ieee80211_local *local, | ||
151 | struct ieee80211_sub_if_data *sdata, | ||
152 | const u8 *bssid, | ||
153 | enum ieee80211_tx_sync_type type) | ||
154 | { | ||
155 | might_sleep(); | ||
156 | |||
157 | trace_drv_finish_tx_sync(local, sdata, bssid, type); | ||
158 | if (local->ops->finish_tx_sync) | ||
159 | local->ops->finish_tx_sync(&local->hw, &sdata->vif, | ||
160 | bssid, type); | ||
161 | trace_drv_return_void(local); | ||
162 | } | ||
163 | |||
133 | static inline u64 drv_prepare_multicast(struct ieee80211_local *local, | 164 | static inline u64 drv_prepare_multicast(struct ieee80211_local *local, |
134 | struct netdev_hw_addr_list *mc_list) | 165 | struct netdev_hw_addr_list *mc_list) |
135 | { | 166 | { |
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 4470f6e8b845..f47b00dc7afd 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h | |||
@@ -319,6 +319,49 @@ TRACE_EVENT(drv_bss_info_changed, | |||
319 | ) | 319 | ) |
320 | ); | 320 | ); |
321 | 321 | ||
322 | DECLARE_EVENT_CLASS(tx_sync_evt, | ||
323 | TP_PROTO(struct ieee80211_local *local, | ||
324 | struct ieee80211_sub_if_data *sdata, | ||
325 | const u8 *bssid, | ||
326 | enum ieee80211_tx_sync_type type), | ||
327 | TP_ARGS(local, sdata, bssid, type), | ||
328 | |||
329 | TP_STRUCT__entry( | ||
330 | LOCAL_ENTRY | ||
331 | VIF_ENTRY | ||
332 | __array(char, bssid, ETH_ALEN) | ||
333 | __field(u32, sync_type) | ||
334 | ), | ||
335 | |||
336 | TP_fast_assign( | ||
337 | LOCAL_ASSIGN; | ||
338 | VIF_ASSIGN; | ||
339 | memcpy(__entry->bssid, bssid, ETH_ALEN); | ||
340 | __entry->sync_type = type; | ||
341 | ), | ||
342 | |||
343 | TP_printk( | ||
344 | LOCAL_PR_FMT VIF_PR_FMT " bssid:%pM type:%d", | ||
345 | LOCAL_PR_ARG, VIF_PR_ARG, __entry->bssid, __entry->sync_type | ||
346 | ) | ||
347 | ); | ||
348 | |||
349 | DEFINE_EVENT(tx_sync_evt, drv_tx_sync, | ||
350 | TP_PROTO(struct ieee80211_local *local, | ||
351 | struct ieee80211_sub_if_data *sdata, | ||
352 | const u8 *bssid, | ||
353 | enum ieee80211_tx_sync_type type), | ||
354 | TP_ARGS(local, sdata, bssid, type) | ||
355 | ); | ||
356 | |||
357 | DEFINE_EVENT(tx_sync_evt, drv_finish_tx_sync, | ||
358 | TP_PROTO(struct ieee80211_local *local, | ||
359 | struct ieee80211_sub_if_data *sdata, | ||
360 | const u8 *bssid, | ||
361 | enum ieee80211_tx_sync_type type), | ||
362 | TP_ARGS(local, sdata, bssid, type) | ||
363 | ); | ||
364 | |||
322 | TRACE_EVENT(drv_prepare_multicast, | 365 | TRACE_EVENT(drv_prepare_multicast, |
323 | TP_PROTO(struct ieee80211_local *local, int mc_count), | 366 | TP_PROTO(struct ieee80211_local *local, int mc_count), |
324 | 367 | ||
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index eb9d4826f775..400c09bea639 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -323,6 +323,7 @@ struct ieee80211_work { | |||
323 | u8 key[WLAN_KEY_LEN_WEP104]; | 323 | u8 key[WLAN_KEY_LEN_WEP104]; |
324 | u8 key_len, key_idx; | 324 | u8 key_len, key_idx; |
325 | bool privacy; | 325 | bool privacy; |
326 | bool synced; | ||
326 | } probe_auth; | 327 | } probe_auth; |
327 | struct { | 328 | struct { |
328 | struct cfg80211_bss *bss; | 329 | struct cfg80211_bss *bss; |
@@ -336,6 +337,7 @@ struct ieee80211_work { | |||
336 | u8 ssid_len; | 337 | u8 ssid_len; |
337 | u8 supp_rates_len; | 338 | u8 supp_rates_len; |
338 | bool wmm_used, use_11n, uapsd_used; | 339 | bool wmm_used, use_11n, uapsd_used; |
340 | bool synced; | ||
339 | } assoc; | 341 | } assoc; |
340 | struct { | 342 | struct { |
341 | u32 duration; | 343 | u32 duration; |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index fee706d39fc2..d6470c7fd6ce 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -2335,14 +2335,16 @@ static enum work_done_result | |||
2335 | ieee80211_probe_auth_done(struct ieee80211_work *wk, | 2335 | ieee80211_probe_auth_done(struct ieee80211_work *wk, |
2336 | struct sk_buff *skb) | 2336 | struct sk_buff *skb) |
2337 | { | 2337 | { |
2338 | struct ieee80211_local *local = wk->sdata->local; | ||
2339 | |||
2338 | if (!skb) { | 2340 | if (!skb) { |
2339 | cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta); | 2341 | cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta); |
2340 | return WORK_DONE_DESTROY; | 2342 | goto destroy; |
2341 | } | 2343 | } |
2342 | 2344 | ||
2343 | if (wk->type == IEEE80211_WORK_AUTH) { | 2345 | if (wk->type == IEEE80211_WORK_AUTH) { |
2344 | cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len); | 2346 | cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len); |
2345 | return WORK_DONE_DESTROY; | 2347 | goto destroy; |
2346 | } | 2348 | } |
2347 | 2349 | ||
2348 | mutex_lock(&wk->sdata->u.mgd.mtx); | 2350 | mutex_lock(&wk->sdata->u.mgd.mtx); |
@@ -2352,6 +2354,12 @@ ieee80211_probe_auth_done(struct ieee80211_work *wk, | |||
2352 | wk->type = IEEE80211_WORK_AUTH; | 2354 | wk->type = IEEE80211_WORK_AUTH; |
2353 | wk->probe_auth.tries = 0; | 2355 | wk->probe_auth.tries = 0; |
2354 | return WORK_DONE_REQUEUE; | 2356 | return WORK_DONE_REQUEUE; |
2357 | destroy: | ||
2358 | if (wk->probe_auth.synced) | ||
2359 | drv_finish_tx_sync(local, wk->sdata, wk->filter_ta, | ||
2360 | IEEE80211_TX_SYNC_AUTH); | ||
2361 | |||
2362 | return WORK_DONE_DESTROY; | ||
2355 | } | 2363 | } |
2356 | 2364 | ||
2357 | int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, | 2365 | int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, |
@@ -2424,6 +2432,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, | |||
2424 | static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, | 2432 | static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, |
2425 | struct sk_buff *skb) | 2433 | struct sk_buff *skb) |
2426 | { | 2434 | { |
2435 | struct ieee80211_local *local = wk->sdata->local; | ||
2427 | struct ieee80211_mgmt *mgmt; | 2436 | struct ieee80211_mgmt *mgmt; |
2428 | struct ieee80211_rx_status *rx_status; | 2437 | struct ieee80211_rx_status *rx_status; |
2429 | struct ieee802_11_elems elems; | 2438 | struct ieee802_11_elems elems; |
@@ -2431,7 +2440,7 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, | |||
2431 | 2440 | ||
2432 | if (!skb) { | 2441 | if (!skb) { |
2433 | cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta); | 2442 | cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta); |
2434 | return WORK_DONE_DESTROY; | 2443 | goto destroy; |
2435 | } | 2444 | } |
2436 | 2445 | ||
2437 | if (wk->type == IEEE80211_WORK_ASSOC_BEACON_WAIT) { | 2446 | if (wk->type == IEEE80211_WORK_ASSOC_BEACON_WAIT) { |
@@ -2451,6 +2460,10 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, | |||
2451 | status = le16_to_cpu(mgmt->u.assoc_resp.status_code); | 2460 | status = le16_to_cpu(mgmt->u.assoc_resp.status_code); |
2452 | 2461 | ||
2453 | if (status == WLAN_STATUS_SUCCESS) { | 2462 | if (status == WLAN_STATUS_SUCCESS) { |
2463 | if (wk->assoc.synced) | ||
2464 | drv_finish_tx_sync(local, wk->sdata, wk->filter_ta, | ||
2465 | IEEE80211_TX_SYNC_ASSOC); | ||
2466 | |||
2454 | mutex_lock(&wk->sdata->u.mgd.mtx); | 2467 | mutex_lock(&wk->sdata->u.mgd.mtx); |
2455 | if (!ieee80211_assoc_success(wk, mgmt, skb->len)) { | 2468 | if (!ieee80211_assoc_success(wk, mgmt, skb->len)) { |
2456 | mutex_unlock(&wk->sdata->u.mgd.mtx); | 2469 | mutex_unlock(&wk->sdata->u.mgd.mtx); |
@@ -2464,6 +2477,11 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, | |||
2464 | } | 2477 | } |
2465 | 2478 | ||
2466 | cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len); | 2479 | cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len); |
2480 | destroy: | ||
2481 | if (wk->assoc.synced) | ||
2482 | drv_finish_tx_sync(local, wk->sdata, wk->filter_ta, | ||
2483 | IEEE80211_TX_SYNC_ASSOC); | ||
2484 | |||
2467 | return WORK_DONE_DESTROY; | 2485 | return WORK_DONE_DESTROY; |
2468 | } | 2486 | } |
2469 | 2487 | ||
diff --git a/net/mac80211/work.c b/net/mac80211/work.c index f0c74a1a9a02..380b9a7462b6 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c | |||
@@ -25,6 +25,7 @@ | |||
25 | 25 | ||
26 | #include "ieee80211_i.h" | 26 | #include "ieee80211_i.h" |
27 | #include "rate.h" | 27 | #include "rate.h" |
28 | #include "driver-ops.h" | ||
28 | 29 | ||
29 | #define IEEE80211_AUTH_TIMEOUT (HZ / 5) | 30 | #define IEEE80211_AUTH_TIMEOUT (HZ / 5) |
30 | #define IEEE80211_AUTH_MAX_TRIES 3 | 31 | #define IEEE80211_AUTH_MAX_TRIES 3 |
@@ -427,6 +428,14 @@ ieee80211_direct_probe(struct ieee80211_work *wk) | |||
427 | struct ieee80211_sub_if_data *sdata = wk->sdata; | 428 | struct ieee80211_sub_if_data *sdata = wk->sdata; |
428 | struct ieee80211_local *local = sdata->local; | 429 | struct ieee80211_local *local = sdata->local; |
429 | 430 | ||
431 | if (!wk->probe_auth.synced) { | ||
432 | int ret = drv_tx_sync(local, sdata, wk->filter_ta, | ||
433 | IEEE80211_TX_SYNC_AUTH); | ||
434 | if (ret) | ||
435 | return WORK_ACT_TIMEOUT; | ||
436 | } | ||
437 | wk->probe_auth.synced = true; | ||
438 | |||
430 | wk->probe_auth.tries++; | 439 | wk->probe_auth.tries++; |
431 | if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { | 440 | if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { |
432 | printk(KERN_DEBUG "%s: direct probe to %pM timed out\n", | 441 | printk(KERN_DEBUG "%s: direct probe to %pM timed out\n", |
@@ -466,6 +475,14 @@ ieee80211_authenticate(struct ieee80211_work *wk) | |||
466 | struct ieee80211_sub_if_data *sdata = wk->sdata; | 475 | struct ieee80211_sub_if_data *sdata = wk->sdata; |
467 | struct ieee80211_local *local = sdata->local; | 476 | struct ieee80211_local *local = sdata->local; |
468 | 477 | ||
478 | if (!wk->probe_auth.synced) { | ||
479 | int ret = drv_tx_sync(local, sdata, wk->filter_ta, | ||
480 | IEEE80211_TX_SYNC_AUTH); | ||
481 | if (ret) | ||
482 | return WORK_ACT_TIMEOUT; | ||
483 | } | ||
484 | wk->probe_auth.synced = true; | ||
485 | |||
469 | wk->probe_auth.tries++; | 486 | wk->probe_auth.tries++; |
470 | if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { | 487 | if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { |
471 | printk(KERN_DEBUG "%s: authentication with %pM" | 488 | printk(KERN_DEBUG "%s: authentication with %pM" |
@@ -499,6 +516,14 @@ ieee80211_associate(struct ieee80211_work *wk) | |||
499 | struct ieee80211_sub_if_data *sdata = wk->sdata; | 516 | struct ieee80211_sub_if_data *sdata = wk->sdata; |
500 | struct ieee80211_local *local = sdata->local; | 517 | struct ieee80211_local *local = sdata->local; |
501 | 518 | ||
519 | if (!wk->assoc.synced) { | ||
520 | int ret = drv_tx_sync(local, sdata, wk->filter_ta, | ||
521 | IEEE80211_TX_SYNC_ASSOC); | ||
522 | if (ret) | ||
523 | return WORK_ACT_TIMEOUT; | ||
524 | } | ||
525 | wk->assoc.synced = true; | ||
526 | |||
502 | wk->assoc.tries++; | 527 | wk->assoc.tries++; |
503 | if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) { | 528 | if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) { |
504 | printk(KERN_DEBUG "%s: association with %pM" | 529 | printk(KERN_DEBUG "%s: association with %pM" |