From 24f6497c344a4f924737102f4931ad7969316349 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 29 Sep 2011 19:22:08 +0300 Subject: [PATCH] GAS: Use off-channel operations for requests This separates off-channel Action frame TX/RX from P2P into a generic implementation that can now be used both for P2P and GAS needs. --- wpa_supplicant/Makefile | 7 + wpa_supplicant/events.c | 42 ++-- wpa_supplicant/gas_query.c | 17 +- wpa_supplicant/offchannel.c | 310 ++++++++++++++++++++++++++++++ wpa_supplicant/offchannel.h | 38 ++++ wpa_supplicant/p2p_supplicant.c | 263 +++---------------------- wpa_supplicant/wpa_supplicant.c | 5 + wpa_supplicant/wpa_supplicant_i.h | 32 ++- 8 files changed, 452 insertions(+), 262 deletions(-) create mode 100644 wpa_supplicant/offchannel.c create mode 100644 wpa_supplicant/offchannel.h diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index fccc4d18f..8bf792755 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -202,6 +202,7 @@ OBJS += ../src/p2p/p2p_group.o OBJS += ../src/ap/p2p_hostapd.o CFLAGS += -DCONFIG_P2P NEED_GAS=y +NEED_OFFCHANNEL=y NEED_80211_COMMON=y ifdef CONFIG_P2P_STRICT CFLAGS += -DCONFIG_P2P_STRICT @@ -1287,6 +1288,12 @@ ifdef NEED_GAS OBJS += ../src/common/gas.o OBJS += gas_query.o CFLAGS += -DCONFIG_GAS +NEED_OFFCHANNEL=y +endif + +ifdef NEED_OFFCHANNEL +OBJS += offchannel.o +CFLAGS += -DCONFIG_OFFCHANNEL endif OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 736ef66d5..7dc084c0a 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -45,6 +45,7 @@ #include "bss.h" #include "mlme.h" #include "scan.h" +#include "offchannel.h" static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) @@ -1914,27 +1915,28 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_assoc_timed_out(wpa_s, data); break; -#ifdef CONFIG_AP case EVENT_TX_STATUS: wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR " type=%d stype=%d", MAC2STR(data->tx_status.dst), data->tx_status.type, data->tx_status.stype); +#ifdef CONFIG_AP if (wpa_s->ap_iface == NULL) { -#ifdef CONFIG_P2P +#ifdef CONFIG_OFFCHANNEL if (data->tx_status.type == WLAN_FC_TYPE_MGMT && data->tx_status.stype == WLAN_FC_STYPE_ACTION) - wpas_send_action_tx_status( + offchannel_send_action_tx_status( wpa_s, data->tx_status.dst, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack ? - P2P_SEND_ACTION_SUCCESS : - P2P_SEND_ACTION_NO_ACK); -#endif /* CONFIG_P2P */ + OFFCHANNEL_SEND_ACTION_SUCCESS : + OFFCHANNEL_SEND_ACTION_NO_ACK); +#endif /* CONFIG_OFFCHANNEL */ break; } -#ifdef CONFIG_P2P +#endif /* CONFIG_AP */ +#ifdef CONFIG_OFFCHANNEL wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst=" MACSTR, MAC2STR(wpa_s->parent->pending_action_dst)); /* @@ -1945,16 +1947,17 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->tx_status.stype == WLAN_FC_STYPE_ACTION && os_memcmp(wpa_s->parent->pending_action_dst, data->tx_status.dst, ETH_ALEN) == 0) { - wpas_send_action_tx_status( + offchannel_send_action_tx_status( wpa_s->parent, data->tx_status.dst, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack ? - P2P_SEND_ACTION_SUCCESS : - P2P_SEND_ACTION_NO_ACK); + OFFCHANNEL_SEND_ACTION_SUCCESS : + OFFCHANNEL_SEND_ACTION_NO_ACK); break; } -#endif /* CONFIG_P2P */ +#endif /* CONFIG_OFFCHANNEL */ +#ifdef CONFIG_AP switch (data->tx_status.type) { case WLAN_FC_TYPE_MGMT: ap_mgmt_tx_cb(wpa_s, data->tx_status.data, @@ -1969,7 +1972,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->tx_status.ack); break; } +#endif /* CONFIG_AP */ break; +#ifdef CONFIG_AP case EVENT_RX_FROM_UNKNOWN: if (wpa_s->ap_iface == NULL) break; @@ -2067,16 +2072,29 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->rx_probe_req.ie_len); #endif /* CONFIG_P2P */ break; -#ifdef CONFIG_P2P case EVENT_REMAIN_ON_CHANNEL: +#ifdef CONFIG_OFFCHANNEL + offchannel_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq, + data->remain_on_channel.duration); +#endif /* CONFIG_OFFCHANNEL */ +#ifdef CONFIG_P2P wpas_p2p_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq, data->remain_on_channel.duration); +#endif /* CONFIG_P2P */ break; case EVENT_CANCEL_REMAIN_ON_CHANNEL: +#ifdef CONFIG_OFFCHANNEL + offchannel_cancel_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq); +#endif /* CONFIG_OFFCHANNEL */ +#ifdef CONFIG_P2P wpas_p2p_cancel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq); +#endif /* CONFIG_P2P */ break; +#ifdef CONFIG_P2P case EVENT_P2P_DEV_FOUND: { struct p2p_peer_info peer_info; diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index befe9fff5..0011017b6 100644 --- a/wpa_supplicant/gas_query.c +++ b/wpa_supplicant/gas_query.c @@ -21,6 +21,7 @@ #include "common/gas.h" #include "wpa_supplicant_i.h" #include "driver_i.h" +#include "offchannel.h" #include "gas_query.h" @@ -32,7 +33,8 @@ struct gas_query_pending { u8 addr[ETH_ALEN]; u8 dialog_token; u8 next_frag_id; - int wait_comeback; + int wait_comeback:1; + int offchannel_tx_started:1; int freq; u16 status_code; struct wpabuf *adv_proto; @@ -73,6 +75,8 @@ static void gas_query_done(struct gas_query *gas, struct gas_query_pending *query, enum gas_query_result result) { + if (query->offchannel_tx_started) + offchannel_send_action_done(gas->wpa_s); eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); eloop_cancel_timeout(gas_query_timeout, gas, query); dl_list_del(&query->list); @@ -127,12 +131,17 @@ static int gas_query_append(struct gas_query_pending *query, const u8 *data, static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, struct wpabuf *req) { + int res; wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " "freq=%d", MAC2STR(query->addr), (unsigned int) wpabuf_len(req), query->freq); - return wpa_drv_send_action(gas->wpa_s, query->freq, 0, query->addr, - gas->wpa_s->own_addr, query->addr, - wpabuf_head(req), wpabuf_len(req)); + res = offchannel_send_action(gas->wpa_s, query->freq, query->addr, + gas->wpa_s->own_addr, query->addr, + wpabuf_head(req), wpabuf_len(req), 1000, + NULL); + if (res == 0) + query->offchannel_tx_started = 1; + return res; } diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c new file mode 100644 index 000000000..8f8ef3a4d --- /dev/null +++ b/wpa_supplicant/offchannel.c @@ -0,0 +1,310 @@ +/* + * wpa_supplicant - Off-channel Action frame TX/RX + * Copyright (c) 2009-2010, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "utils/eloop.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "offchannel.h" + + + +static struct wpa_supplicant * +wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src) +{ + struct wpa_supplicant *iface; + + if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0) + return wpa_s; + + /* + * Try to find a group interface that matches with the source address. + */ + iface = wpa_s->global->ifaces; + while (iface) { + if (os_memcmp(wpa_s->pending_action_src, + iface->own_addr, ETH_ALEN) == 0) + break; + iface = iface->next; + } + if (iface) { + wpa_printf(MSG_DEBUG, "P2P: Use group interface %s " + "instead of interface %s for Action TX", + iface->ifname, wpa_s->ifname); + return iface; + } + + return wpa_s; +} + + +static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_supplicant *iface; + int res; + int without_roc; + + without_roc = wpa_s->pending_action_without_roc; + wpa_s->pending_action_without_roc = 0; + wpa_printf(MSG_DEBUG, "Off-channel: Send Action callback " + "(without_roc=%d pending_action_tx=%p)", + without_roc, wpa_s->pending_action_tx); + + if (wpa_s->pending_action_tx == NULL) + return; + + /* + * This call is likely going to be on the P2P device instance if the + * driver uses a separate interface for that purpose. However, some + * Action frames are actually sent within a P2P Group and when that is + * the case, we need to follow power saving (e.g., GO buffering the + * frame for a client in PS mode or a client following the advertised + * NoA from its GO). To make that easier for the driver, select the + * correct group interface here. + */ + iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src); + + if (wpa_s->off_channel_freq != wpa_s->pending_action_freq && + wpa_s->pending_action_freq != 0 && + wpa_s->pending_action_freq != iface->assoc_freq) { + wpa_printf(MSG_DEBUG, "Off-channel: Pending Action frame TX " + "waiting for another freq=%u (off_channel_freq=%u " + "assoc_freq=%u)", + wpa_s->pending_action_freq, + wpa_s->off_channel_freq, + iface->assoc_freq); + if (without_roc && wpa_s->off_channel_freq == 0) { + /* + * We may get here if wpas_send_action() found us to be + * on the correct channel, but remain-on-channel cancel + * event was received before getting here. + */ + wpa_printf(MSG_DEBUG, "Off-channel: Schedule " + "remain-on-channel to send Action frame"); + if (wpa_drv_remain_on_channel( + wpa_s, wpa_s->pending_action_freq, 200) < + 0) { + wpa_printf(MSG_DEBUG, "Off-channel: Failed to " + "request driver to remain on " + "channel (%u MHz) for Action Frame " + "TX", wpa_s->pending_action_freq); + } else { + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = + wpa_s->pending_action_freq; + } + } + return; + } + + wpa_printf(MSG_DEBUG, "Off-channel: 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, 0, + 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 (res) { + wpa_printf(MSG_DEBUG, "Off-channel: Failed to send the " + "pending Action frame"); + /* + * Use fake TX status event to allow state machines to + * continue. + */ + offchannel_send_action_tx_status( + wpa_s, wpa_s->pending_action_dst, + wpabuf_head(wpa_s->pending_action_tx), + wpabuf_len(wpa_s->pending_action_tx), + OFFCHANNEL_SEND_ACTION_FAILED); + } +} + + +void offchannel_send_action_tx_status( + struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data, + size_t data_len, enum offchannel_send_action_result result) +{ + if (wpa_s->pending_action_tx == NULL) { + wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - " + "no pending operation"); + return; + } + + if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - " + "unknown destination address"); + return; + } + + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; + + wpa_printf(MSG_DEBUG, "Off-channel: TX status result=%d cb=%p", + result, wpa_s->pending_action_tx_status_cb); + + if (wpa_s->pending_action_tx_status_cb) { + wpa_s->pending_action_tx_status_cb( + wpa_s, wpa_s->pending_action_freq, + wpa_s->pending_action_dst, wpa_s->pending_action_src, + wpa_s->pending_action_bssid, + data, data_len, result); + } +} + + +int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, + const u8 *dst, const u8 *src, const u8 *bssid, + const u8 *buf, size_t len, unsigned int wait_time, + void (*tx_cb)(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result + result)) +{ + wpa_printf(MSG_DEBUG, "Off-channel: Send action frame: freq=%d dst=" + MACSTR " src=" MACSTR " bssid=" MACSTR " len=%d", + freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), + (int) len); + + wpa_s->pending_action_tx_status_cb = tx_cb; + + if (wpa_s->pending_action_tx) { + wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action " + "frame TX to " MACSTR, + MAC2STR(wpa_s->pending_action_dst)); + wpabuf_free(wpa_s->pending_action_tx); + } + wpa_s->pending_action_tx = wpabuf_alloc(len); + if (wpa_s->pending_action_tx == NULL) { + wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action " + "frame TX buffer (len=%llu)", + (unsigned long long) len); + return -1; + } + wpabuf_put_data(wpa_s->pending_action_tx, buf, len); + os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN); + os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN); + 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); + if (tx_iface->assoc_freq == freq) { + wpa_printf(MSG_DEBUG, "Off-channel: Already on " + "requested channel (TX interface operating " + "channel)"); + freq = 0; + } + } + + if (wpa_s->off_channel_freq == freq || freq == 0) { + wpa_printf(MSG_DEBUG, "Off-channel: Already on requested " + "channel; send Action frame immediately"); + /* TODO: Would there ever be need to extend the current + * duration on the channel? */ + wpa_s->pending_action_without_roc = 1; + eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL); + eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL); + return 0; + } + wpa_s->pending_action_without_roc = 0; + + if (wpa_s->roc_waiting_drv_freq == freq) { + wpa_printf(MSG_DEBUG, "Off-channel: Already waiting for " + "driver to get to frequency %u MHz; continue " + "waiting to send the Action frame", freq); + return 0; + } + + wpa_printf(MSG_DEBUG, "Off-channel: Schedule Action frame to be " + "transmitted once the driver gets to the requested " + "channel"); + if (wait_time > wpa_s->max_remain_on_chan) + wait_time = wpa_s->max_remain_on_chan; + if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) { + wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver " + "to remain on channel (%u MHz) for Action " + "Frame TX", freq); + return -1; + } + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = freq; + + return 0; +} + + +void offchannel_send_action_done(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done " + "notification"); + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX && + wpa_s->action_tx_wait_time) + wpa_drv_send_action_cancel_wait(wpa_s); + + 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; + } +} + + +void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, unsigned int duration) +{ + wpa_s->roc_waiting_drv_freq = 0; + wpa_s->off_channel_freq = freq; + wpas_send_action_cb(wpa_s, NULL); +} + + +void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + wpa_s->off_channel_freq = 0; +} + + +void offchannel_deinit(struct wpa_supplicant *wpa_s) +{ + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; + eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL); +} diff --git a/wpa_supplicant/offchannel.h b/wpa_supplicant/offchannel.h new file mode 100644 index 000000000..90f79c145 --- /dev/null +++ b/wpa_supplicant/offchannel.h @@ -0,0 +1,38 @@ +/* + * wpa_supplicant - Off-channel Action frame TX/RX + * Copyright (c) 2009-2010, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef OFFCHANNEL_H +#define OFFCHANNEL_H + +int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, + const u8 *dst, const u8 *src, const u8 *bssid, + const u8 *buf, size_t len, unsigned int wait_time, + void (*tx_cb)(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result + result)); +void offchannel_send_action_done(struct wpa_supplicant *wpa_s); +void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, unsigned int duration); +void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq); +void offchannel_deinit(struct wpa_supplicant *wpa_s); +void offchannel_send_action_tx_status( + struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data, + size_t data_len, enum offchannel_send_action_result result); + +#endif /* OFFCHANNEL_H */ diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 7d3bac181..92bfc41db 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -34,6 +34,7 @@ #include "notify.h" #include "scan.h" #include "bss.h" +#include "offchannel.h" #include "wps_supplicant.h" #include "p2p_supplicant.h" @@ -570,155 +571,38 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, } -static struct wpa_supplicant * -wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src) +static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s, + unsigned int freq, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result + result) { - struct wpa_supplicant *iface; + enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS; - if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0) - return wpa_s; - - /* - * Try to find a group interface that matches with the source address. - */ - iface = wpa_s->global->ifaces; - while (iface) { - if (os_memcmp(wpa_s->pending_action_src, - iface->own_addr, ETH_ALEN) == 0) - break; - iface = iface->next; - } - if (iface) { - wpa_printf(MSG_DEBUG, "P2P: Use group interface %s " - "instead of interface %s for Action TX", - iface->ifname, wpa_s->ifname); - return iface; - } - - return wpa_s; -} - - -static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_supplicant *wpa_s = eloop_ctx; - struct wpa_supplicant *iface; - int res; - int without_roc; - - without_roc = wpa_s->pending_action_without_roc; - wpa_s->pending_action_without_roc = 0; - wpa_printf(MSG_DEBUG, "P2P: Send Action callback (without_roc=%d " - "pending_action_tx=%p)", - without_roc, wpa_s->pending_action_tx); - - if (wpa_s->pending_action_tx == NULL) - return; - - /* - * This call is likely going to be on the P2P device instance if the - * driver uses a separate interface for that purpose. However, some - * Action frames are actually sent within a P2P Group and when that is - * the case, we need to follow power saving (e.g., GO buffering the - * frame for a client in PS mode or a client following the advertised - * NoA from its GO). To make that easier for the driver, select the - * correct group interface here. - */ - iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src); - - if (wpa_s->off_channel_freq != wpa_s->pending_action_freq && - wpa_s->pending_action_freq != 0 && - wpa_s->pending_action_freq != iface->assoc_freq) { - wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX " - "waiting for another freq=%u (off_channel_freq=%u " - "assoc_freq=%u)", - wpa_s->pending_action_freq, - wpa_s->off_channel_freq, - iface->assoc_freq); - if (without_roc && wpa_s->off_channel_freq == 0) { - /* - * We may get here if wpas_send_action() found us to be - * on the correct channel, but remain-on-channel cancel - * event was received before getting here. - */ - wpa_printf(MSG_DEBUG, "P2P: Schedule " - "remain-on-channel to send Action frame"); - if (wpa_drv_remain_on_channel( - wpa_s, wpa_s->pending_action_freq, 200) < - 0) { - wpa_printf(MSG_DEBUG, "P2P: Failed to request " - "driver to remain on channel (%u " - "MHz) for Action Frame TX", - wpa_s->pending_action_freq); - } else { - wpa_s->off_channel_freq = 0; - wpa_s->roc_waiting_drv_freq = - wpa_s->pending_action_freq; - } - } - return; - } - - 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, 0, - 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 (res) { - wpa_printf(MSG_DEBUG, "P2P: Failed to send the pending " - "Action frame"); - /* - * Use fake TX status event to allow P2P state machine to - * continue. - */ - wpas_send_action_tx_status( - wpa_s, wpa_s->pending_action_dst, - wpabuf_head(wpa_s->pending_action_tx), - wpabuf_len(wpa_s->pending_action_tx), - P2P_SEND_ACTION_FAILED); - } -} - - -void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst, - const u8 *data, size_t data_len, - enum p2p_send_action_result result) -{ if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) return; if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) return; - if (wpa_s->pending_action_tx == NULL) { - wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - no " - "pending operation"); - return; + switch (result) { + case OFFCHANNEL_SEND_ACTION_SUCCESS: + res = P2P_SEND_ACTION_SUCCESS; + break; + case OFFCHANNEL_SEND_ACTION_NO_ACK: + res = P2P_SEND_ACTION_NO_ACK; + break; + case OFFCHANNEL_SEND_ACTION_FAILED: + res = P2P_SEND_ACTION_FAILED; + break; } - if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - unknown " - "destination address"); - return; - } - - wpabuf_free(wpa_s->pending_action_tx); - wpa_s->pending_action_tx = NULL; - - p2p_send_action_cb(wpa_s->global->p2p, wpa_s->pending_action_freq, - wpa_s->pending_action_dst, - wpa_s->pending_action_src, - wpa_s->pending_action_bssid, - result); + p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res); if (wpa_s->pending_pd_before_join && - (os_memcmp(wpa_s->pending_action_dst, wpa_s->pending_join_dev_addr, - ETH_ALEN) == 0 || - os_memcmp(wpa_s->pending_action_dst, - wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) { + (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 || + os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) { wpa_s->pending_pd_before_join = 0; wpa_printf(MSG_DEBUG, "P2P: Starting pending " "join-existing-group operation"); @@ -732,104 +616,16 @@ static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst, size_t len, unsigned int wait_time) { struct wpa_supplicant *wpa_s = ctx; - - wpa_printf(MSG_DEBUG, "P2P: Send action frame: freq=%d dst=" MACSTR - " src=" MACSTR " bssid=" MACSTR " len=%d", - freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), - (int) len); - - if (wpa_s->pending_action_tx) { - wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX " - "to " MACSTR, MAC2STR(wpa_s->pending_action_dst)); - wpabuf_free(wpa_s->pending_action_tx); - } - wpa_s->pending_action_tx = wpabuf_alloc(len); - if (wpa_s->pending_action_tx == NULL) { - wpa_printf(MSG_DEBUG, "P2P: Failed to allocate Action frame " - "TX buffer (len=%llu)", (unsigned long long) len); - return -1; - } - wpabuf_put_data(wpa_s->pending_action_tx, buf, len); - os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN); - os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN); - 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); - if (tx_iface->assoc_freq == freq) { - wpa_printf(MSG_DEBUG, "P2P: Already on requested " - "channel (TX interface operating channel)"); - freq = 0; - } - } - - if (wpa_s->off_channel_freq == freq || freq == 0) { - wpa_printf(MSG_DEBUG, "P2P: Already on requested channel; " - "send Action frame immediately"); - /* TODO: Would there ever be need to extend the current - * duration on the channel? */ - wpa_s->pending_action_without_roc = 1; - eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL); - eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL); - return 0; - } - wpa_s->pending_action_without_roc = 0; - - if (wpa_s->roc_waiting_drv_freq == freq) { - wpa_printf(MSG_DEBUG, "P2P: Already waiting for driver to get " - "to frequency %u MHz; continue waiting to send the " - "Action frame", freq); - return 0; - } - - wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted " - "once the driver gets to the requested channel"); - if (wait_time > wpa_s->max_remain_on_chan) - wait_time = wpa_s->max_remain_on_chan; - if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) { - wpa_printf(MSG_DEBUG, "P2P: Failed to request driver " - "to remain on channel (%u MHz) for Action " - "Frame TX", freq); - return -1; - } - wpa_s->off_channel_freq = 0; - wpa_s->roc_waiting_drv_freq = freq; - - return 0; + return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len, + wait_time, + wpas_p2p_send_action_tx_status); } static void wpas_send_action_done(void *ctx) { struct wpa_supplicant *wpa_s = 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->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX && - wpa_s->action_tx_wait_time) - wpa_drv_send_action_cancel_wait(wpa_s); - - 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; - } + offchannel_send_action_done(wpa_s); } @@ -2529,9 +2325,6 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s) wpa_drv_probe_req_report(wpa_s, 0); os_free(wpa_s->go_params); wpa_s->go_params = NULL; - wpabuf_free(wpa_s->pending_action_tx); - wpa_s->pending_action_tx = NULL; - eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); wpa_s->p2p_long_listen = 0; @@ -3056,9 +2849,6 @@ void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; - wpa_s->roc_waiting_drv_freq = 0; - wpa_s->off_channel_freq = freq; - wpas_send_action_cb(wpa_s, NULL); if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) { p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq, wpa_s->pending_listen_duration); @@ -3096,7 +2886,6 @@ void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback " "(p2p_long_listen=%d ms pending_action_tx=%p)", wpa_s->p2p_long_listen, wpa_s->pending_action_tx); - wpa_s->off_channel_freq = 0; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; if (p2p_listen_end(wpa_s->global->p2p, freq) > 0) diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 7de5b5007..d2e29c0d8 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -50,6 +50,7 @@ #include "bgscan.h" #include "bss.h" #include "scan.h" +#include "offchannel.h" const char *wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" @@ -440,6 +441,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpas_p2p_deinit(wpa_s); #endif /* CONFIG_P2P */ +#ifdef CONFIG_OFFCHANNEL + offchannel_deinit(wpa_s); +#endif /* CONFIG_OFFCHANNEL */ + os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index ef1ba11d2..866649bc8 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -322,6 +322,13 @@ struct wpa_client_mlme { #endif /* CONFIG_CLIENT_MLME */ }; +enum offchannel_send_action_result { + OFFCHANNEL_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */, + OFFCHANNEL_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged + */, + OFFCHANNEL_SEND_ACTION_FAILED /* Frame was not sent due to a failure */ +}; + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -478,13 +485,6 @@ struct wpa_supplicant { void *ap_configured_cb_data; #endif /* CONFIG_AP */ -#ifdef CONFIG_P2P - struct p2p_go_neg_results *go_params; - int create_p2p_iface; - u8 pending_interface_addr[ETH_ALEN]; - char pending_interface_name[100]; - int pending_interface_type; - int p2p_group_idx; unsigned int off_channel_freq; struct wpabuf *pending_action_tx; u8 pending_action_src[ETH_ALEN]; @@ -492,6 +492,22 @@ struct wpa_supplicant { u8 pending_action_bssid[ETH_ALEN]; unsigned int pending_action_freq; int pending_action_without_roc; + void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result + result); + unsigned int roc_waiting_drv_freq; + int action_tx_wait_time; + +#ifdef CONFIG_P2P + struct p2p_go_neg_results *go_params; + int create_p2p_iface; + u8 pending_interface_addr[ETH_ALEN]; + char pending_interface_name[100]; + int pending_interface_type; + int p2p_group_idx; unsigned int pending_listen_freq; unsigned int pending_listen_duration; enum { @@ -515,8 +531,6 @@ struct wpa_supplicant { u8 pending_join_dev_addr[ETH_ALEN]; 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; /*