GAS: Use off-channel operations for requests
authorJouni Malinen <jouni@qca.qualcomm.com>
Thu, 29 Sep 2011 16:22:08 +0000 (19:22 +0300)
committerJouni Malinen <j@w1.fi>
Thu, 29 Sep 2011 19:19:51 +0000 (22:19 +0300)
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
wpa_supplicant/events.c
wpa_supplicant/gas_query.c
wpa_supplicant/offchannel.c [new file with mode: 0644]
wpa_supplicant/offchannel.h [new file with mode: 0644]
wpa_supplicant/p2p_supplicant.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index fccc4d1..8bf7927 100644 (file)
@@ -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
index 736ef66..7dc084c 100644 (file)
@@ -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;
 
index befe9ff..0011017 100644 (file)
@@ -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 (file)
index 0000000..8f8ef3a
--- /dev/null
@@ -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 (file)
index 0000000..90f79c1
--- /dev/null
@@ -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 */
index 7d3bac1..92bfc41 100644 (file)
@@ -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)
-{
-       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)
+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 *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);
+       enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS;
 
-       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;
-       }
-
-       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;
+       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;
        }
 
-       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)
index 7de5b50..d2e29c0 100644 (file)
@@ -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;
 
index ef1ba11..866649b 100644 (file)
@@ -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;
 
        /*