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.
This commit is contained in:
parent
04ea7b7947
commit
24f6497c34
8 changed files with 452 additions and 262 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
310
wpa_supplicant/offchannel.c
Normal file
310
wpa_supplicant/offchannel.c
Normal file
|
@ -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);
|
||||
}
|
38
wpa_supplicant/offchannel.h
Normal file
38
wpa_supplicant/offchannel.h
Normal file
|
@ -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 */
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue