diff options
author | Johannes Berg <johannes.berg@intel.com> | 2011-07-19 04:39:53 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-07-20 15:04:35 -0400 |
commit | b2abb6e2bcb91ae384c5857dffd0bb97b76c7a68 (patch) | |
tree | 4f6381c0a7b4dc8a1f9cd3c3bf2b94a6c971c737 | |
parent | e0d687bd9df218ba3d97aac15919d30816d72dcb (diff) |
mac80211: sync driver before TX
In P2P client mode, the GO (AP) to connect to might
have periods of time where it is not available due
to powersave. To allow the driver to sync with it
and send frames to the GO only when it is available
add a new callback tx_sync (and the corresponding
finish_tx_sync). These callbacks can sleep unlike
the actual TX.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-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" |