diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 728ef79ca..d3b285cc4 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -559,6 +559,8 @@ struct wpa_driver_capa { * operation does not end up getting completed successfully later. */ #define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000 +/* Driver supports off-channel TX */ +#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000 unsigned int flags; int max_scan_ssids; @@ -1724,6 +1726,7 @@ struct wpa_driver_ops { * send_action - Transmit an Action frame * @priv: Private driver interface data * @freq: Frequency (in MHz) of the channel + * @wait: Time to wait off-channel for a response (in ms), or zero * @dst: Destination MAC address (Address 1) * @src: Source MAC address (Address 2) * @bssid: BSSID (Address 3) @@ -1732,16 +1735,32 @@ struct wpa_driver_ops { * Returns: 0 on success, -1 on failure * * This command can be used to request the driver to transmit an action - * frame to the specified destination. If a remain-on-channel duration - * is in progress, the frame is transmitted on that channel. Otherwise, - * the frame is transmitted on the current operational channel if in - * associated state in station mode or if operating as an AP. If none - * of these conditions is in effect, send_action() cannot be used. + * frame to the specified destination. + * + * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will + * be transmitted on the given channel and the device will wait for a + * response on that channel for the given wait time. + * + * If the flag is not set, the wait time will be ignored. In this case, + * if a remain-on-channel duration is in progress, the frame must be + * transmitted on that channel; alternatively the frame may be sent on + * the current operational channel (if in associated state in station + * mode or while operating as an AP.) */ - int (*send_action)(void *priv, unsigned int freq, + int (*send_action)(void *priv, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len); + /** + * send_action_cancel_wait - Cancel action frame TX wait + * @priv: Private driver interface data + * + * This command cancels the wait time associated with sending an action + * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is + * set in the driver flags. + */ + void (*send_action_cancel_wait)(void *priv); + /** * remain_on_channel - Remain awake on a channel * @priv: Private driver interface data diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index 34bfa2bc0..56359c1ef 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -3295,6 +3295,7 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = { NULL /* set_supp_port */, NULL /* set_wds_sta */, NULL /* send_action */, + NULL /* send_action_cancel_wait */, NULL /* remain_on_channel */, NULL /* cancel_remain_on_channel */, NULL /* probe_req_report */, diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index f7920990e..f2e5a744f 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -5835,6 +5835,7 @@ nla_put_failure: static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, + unsigned int wait_time, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len) @@ -6146,8 +6147,8 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, pos += ETH_ALEN; os_memcpy(pos, ies, ies_len); - ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, drv->bssid, - own_addr, drv->bssid, + ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0, + drv->bssid, own_addr, drv->bssid, data, data_len); os_free(data); diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c index c630e4c2c..2741182f3 100644 --- a/src/drivers/driver_test.c +++ b/src/drivers/driver_test.c @@ -2598,6 +2598,7 @@ static int wpa_driver_test_set_freq(void *priv, static int wpa_driver_test_send_action(void *priv, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len) diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index af8232a76..34364810f 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -383,17 +383,24 @@ static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s, static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len) { if (wpa_s->driver->send_action) return wpa_s->driver->send_action(wpa_s->drv_priv, freq, - dst, src, bssid, data, - data_len); + wait, dst, src, bssid, + data, data_len); return -1; } +static inline void wpa_drv_send_action_cancel_wait(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->send_action_cancel_wait) + wpa_s->driver->send_action_cancel_wait(wpa_s->drv_priv); +} + static inline int wpa_drv_set_freq(struct wpa_supplicant *wpa_s, struct hostapd_freq_params *freq) { diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index d08492f95..37455515f 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -614,7 +614,7 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to " MACSTR " using interface %s", MAC2STR(wpa_s->pending_action_dst), iface->ifname); - res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, + res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0, wpa_s->pending_action_dst, wpa_s->pending_action_src, wpa_s->pending_action_bssid, @@ -705,6 +705,20 @@ static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst, os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN); wpa_s->pending_action_freq = freq; + if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) { + struct wpa_supplicant *iface; + + iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src); + wpa_s->action_tx_wait_time = wait_time; + + return wpa_drv_send_action(iface, wpa_s->pending_action_freq, + wait_time, wpa_s->pending_action_dst, + wpa_s->pending_action_src, + wpa_s->pending_action_bssid, + wpabuf_head(wpa_s->pending_action_tx), + wpabuf_len(wpa_s->pending_action_tx)); + } + if (freq) { struct wpa_supplicant *tx_iface; tx_iface = wpas_get_tx_interface(wpa_s, src); @@ -757,7 +771,11 @@ static void wpas_send_action_done(void *ctx) wpa_printf(MSG_DEBUG, "P2P: Action frame sequence done notification"); wpabuf_free(wpa_s->pending_action_tx); wpa_s->pending_action_tx = NULL; - if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) { + if (wpa_s->action_tx_wait_time) + wpa_drv_send_action_cancel_wait(wpa_s); + wpa_s->off_channel_freq = 0; + } else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { wpa_drv_cancel_remain_on_channel(wpa_s); wpa_s->off_channel_freq = 0; wpa_s->roc_waiting_drv_freq = 0; diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 018b37283..60c67522d 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -538,7 +538,7 @@ static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s, req[0] = WLAN_ACTION_SA_QUERY; req[1] = WLAN_SA_QUERY_REQUEST; os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN); - if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, wpa_s->bssid, + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, req, sizeof(req)) < 0) wpa_printf(MSG_INFO, "SME: Failed to send SA Query Request"); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index a6c4a9a44..9fdd79910 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -503,6 +503,7 @@ struct wpa_supplicant { int pending_join_wps_method; int p2p_join_scan_count; unsigned int roc_waiting_drv_freq; + int action_tx_wait_time; int force_long_sd; /*