Add driver API functionality for off-channel Action frames
This adds new commands and events for allowing off-channel Action frame exchanges to be requested. This functionality is not yet used and is only fully supported by driver_test.c at this point. driver_nl80211.c has support for the remain-on-channel commands, but the Action frame TX/RX part is still pending review for the kernel code and as such, is not yet included here.
This commit is contained in:
parent
d7c53e432b
commit
55777702cd
5 changed files with 430 additions and 0 deletions
|
@ -1519,6 +1519,27 @@ struct wpa_driver_ops {
|
||||||
*/
|
*/
|
||||||
int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val);
|
int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* send_action - Transmit an Action frame
|
||||||
|
* @priv: Private driver interface data
|
||||||
|
* @freq: Frequency (in MHz) of the channel
|
||||||
|
* @dst: Destination MAC address
|
||||||
|
* @src: Source MAC address
|
||||||
|
* @data: Frame body
|
||||||
|
* @data_len: data length in octets
|
||||||
|
* Returns: 0 on success, -1 on failure
|
||||||
|
*
|
||||||
|
* This command can be used to request the driver to transmit an action
|
||||||
|
* frame to the specified destination. If a remain-on-channel duration
|
||||||
|
* is in progress, the frame is transmitted on that channel. Otherwise,
|
||||||
|
* the frame is transmitted on the current operational channel if in
|
||||||
|
* associated state in station mode or if operating as an AP. If none
|
||||||
|
* of these conditions is in effect, send_action() cannot be used.
|
||||||
|
*/
|
||||||
|
int (*send_action)(void *priv, unsigned int freq,
|
||||||
|
const u8 *dst, const u8 *src,
|
||||||
|
const u8 *data, size_t data_len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* alloc_interface_addr - Allocate a virtual interface address
|
* alloc_interface_addr - Allocate a virtual interface address
|
||||||
* @priv: Private driver interface data
|
* @priv: Private driver interface data
|
||||||
|
@ -1549,6 +1570,44 @@ struct wpa_driver_ops {
|
||||||
*/
|
*/
|
||||||
void (*release_interface_addr)(void *priv, const u8 *addr);
|
void (*release_interface_addr)(void *priv, const u8 *addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remain_on_channel - Remain awake on a channel
|
||||||
|
* @priv: Private driver interface data
|
||||||
|
* @freq: Frequency (in MHz) of the channel
|
||||||
|
* @duration: Duration in milliseconds
|
||||||
|
* Returns: 0 on success, -1 on failure
|
||||||
|
*
|
||||||
|
* This command is used to request the driver to remain awake on the
|
||||||
|
* specified channel for the specified duration and report received
|
||||||
|
* Action frames with EVENT_RX_ACTION events. Optionally, received
|
||||||
|
* Probe Request frames may also be requested to be reported by calling
|
||||||
|
* probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ.
|
||||||
|
*
|
||||||
|
* The driver may not be at the requested channel when this function
|
||||||
|
* returns, i.e., the return code is only indicating whether the
|
||||||
|
* request was accepted. The caller will need to wait until the
|
||||||
|
* EVENT_REMAIN_ON_CHANNEL event indicates that the driver has
|
||||||
|
* completed the channel change. This may take some time due to other
|
||||||
|
* need for the radio and the caller should be prepared to timing out
|
||||||
|
* its wait since there are no guarantees on when this request can be
|
||||||
|
* executed.
|
||||||
|
*/
|
||||||
|
int (*remain_on_channel)(void *priv, unsigned int freq,
|
||||||
|
unsigned int duration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cancel_remain_on_channel - Cancel remain-on-channel operation
|
||||||
|
* @priv: Private driver interface data
|
||||||
|
*
|
||||||
|
* This command can be used to cancel a remain-on-channel operation
|
||||||
|
* before its originally requested duration has passed. This could be
|
||||||
|
* used, e.g., when remain_on_channel() is used to request extra time
|
||||||
|
* to receive a response to an Action frame and the response is
|
||||||
|
* received when there is still unneeded time remaining on the
|
||||||
|
* remain-on-channel operation.
|
||||||
|
*/
|
||||||
|
int (*cancel_remain_on_channel)(void *priv);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* probe_req_report - Request Probe Request frames to be indicated
|
* probe_req_report - Request Probe Request frames to be indicated
|
||||||
* @priv: Private driver interface data
|
* @priv: Private driver interface data
|
||||||
|
@ -1766,6 +1825,34 @@ enum wpa_event_type {
|
||||||
*/
|
*/
|
||||||
EVENT_RX_MGMT,
|
EVENT_RX_MGMT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EVENT_RX_ACTION - Action frame received
|
||||||
|
*
|
||||||
|
* This event is used to indicate when an Action frame has been
|
||||||
|
* received. Information about the received frame is included in
|
||||||
|
* union wpa_event_data::rx_action.
|
||||||
|
*/
|
||||||
|
EVENT_RX_ACTION,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started
|
||||||
|
*
|
||||||
|
* This event is used to indicate when the driver has started the
|
||||||
|
* requested remain-on-channel duration. Information about the
|
||||||
|
* operation is included in union wpa_event_data::remain_on_channel.
|
||||||
|
*/
|
||||||
|
EVENT_REMAIN_ON_CHANNEL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out
|
||||||
|
*
|
||||||
|
* This event is used to indicate when the driver has completed
|
||||||
|
* remain-on-channel duration, i.e., may noot be available on the
|
||||||
|
* requested channel anymore. Information about the
|
||||||
|
* operation is included in union wpa_event_data::remain_on_channel.
|
||||||
|
*/
|
||||||
|
EVENT_CANCEL_REMAIN_ON_CHANNEL,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EVENT_MLME_RX - Report reception of frame for MLME (test use only)
|
* EVENT_MLME_RX - Report reception of frame for MLME (test use only)
|
||||||
*
|
*
|
||||||
|
@ -2011,6 +2098,53 @@ union wpa_event_data {
|
||||||
u32 ssi_signal;
|
u32 ssi_signal;
|
||||||
} rx_mgmt;
|
} rx_mgmt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rx_action - Data for EVENT_RX_ACTION events
|
||||||
|
*/
|
||||||
|
struct rx_action {
|
||||||
|
/**
|
||||||
|
* sa - Source address of the received Action frame
|
||||||
|
*/
|
||||||
|
const u8 *sa;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* category - Action frame category
|
||||||
|
*/
|
||||||
|
u8 category;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* data - Action frame body after category field
|
||||||
|
*/
|
||||||
|
const u8 *data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* len - Length of data in octets
|
||||||
|
*/
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* freq - Frequency (in MHz) on which the frame was received
|
||||||
|
*/
|
||||||
|
int freq;
|
||||||
|
} rx_action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events
|
||||||
|
*
|
||||||
|
* This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events.
|
||||||
|
*/
|
||||||
|
struct remain_on_channel {
|
||||||
|
/**
|
||||||
|
* freq - Channel frequency in MHz
|
||||||
|
*/
|
||||||
|
unsigned int freq;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* duration - Duration to remain on the channel in milliseconds
|
||||||
|
*/
|
||||||
|
unsigned int duration;
|
||||||
|
} remain_on_channel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct scan_info - Optional data for EVENT_SCAN_RESULTS events
|
* struct scan_info - Optional data for EVENT_SCAN_RESULTS events
|
||||||
* @aborted: Whether the scan was aborted
|
* @aborted: Whether the scan was aborted
|
||||||
|
|
|
@ -3246,7 +3246,10 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = {
|
||||||
NULL /* set_ap_wps_ie */,
|
NULL /* set_ap_wps_ie */,
|
||||||
NULL /* set_supp_port */,
|
NULL /* set_supp_port */,
|
||||||
NULL /* set_wds_sta */,
|
NULL /* set_wds_sta */,
|
||||||
|
NULL /* send_action */,
|
||||||
NULL /* alloc_interface_addr */,
|
NULL /* alloc_interface_addr */,
|
||||||
NULL /* release_interface_addr */,
|
NULL /* release_interface_addr */,
|
||||||
|
NULL /* remain_on_channel */,
|
||||||
|
NULL /* cancel_remain_on_channel */,
|
||||||
NULL /* probe_req_report */
|
NULL /* probe_req_report */
|
||||||
};
|
};
|
||||||
|
|
|
@ -98,6 +98,9 @@ struct wpa_driver_nl80211_data {
|
||||||
int probe_req_report;
|
int probe_req_report;
|
||||||
|
|
||||||
unsigned int beacon_set:1;
|
unsigned int beacon_set:1;
|
||||||
|
unsigned int pending_remain_on_chan:1;
|
||||||
|
|
||||||
|
u64 remain_on_chan_cookie;
|
||||||
|
|
||||||
#ifdef HOSTAPD
|
#ifdef HOSTAPD
|
||||||
int eapol_sock; /* socket for EAPOL frames */
|
int eapol_sock; /* socket for EAPOL frames */
|
||||||
|
@ -709,6 +712,53 @@ static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
|
||||||
|
int cancel_event, struct nlattr *tb[])
|
||||||
|
{
|
||||||
|
unsigned int freq, chan_type, duration;
|
||||||
|
union wpa_event_data data;
|
||||||
|
u64 cookie;
|
||||||
|
|
||||||
|
if (tb[NL80211_ATTR_WIPHY_FREQ])
|
||||||
|
freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
|
||||||
|
else
|
||||||
|
freq = 0;
|
||||||
|
|
||||||
|
if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
|
||||||
|
chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
|
||||||
|
else
|
||||||
|
chan_type = 0;
|
||||||
|
|
||||||
|
if (tb[NL80211_ATTR_DURATION])
|
||||||
|
duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
|
||||||
|
else
|
||||||
|
duration = 0;
|
||||||
|
|
||||||
|
if (tb[NL80211_ATTR_COOKIE])
|
||||||
|
cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
|
||||||
|
else
|
||||||
|
cookie = 0;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
|
||||||
|
"freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
|
||||||
|
cancel_event, freq, chan_type, duration,
|
||||||
|
(long long unsigned int) cookie,
|
||||||
|
cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
|
||||||
|
|
||||||
|
if (cookie != drv->remain_on_chan_cookie)
|
||||||
|
return; /* not for us */
|
||||||
|
|
||||||
|
drv->pending_remain_on_chan = !cancel_event;
|
||||||
|
|
||||||
|
os_memset(&data, 0, sizeof(data));
|
||||||
|
data.remain_on_channel.freq = freq;
|
||||||
|
data.remain_on_channel.duration = duration;
|
||||||
|
wpa_supplicant_event(drv->ctx, cancel_event ?
|
||||||
|
EVENT_CANCEL_REMAIN_ON_CHANNEL :
|
||||||
|
EVENT_REMAIN_ON_CHANNEL, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
|
static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
|
||||||
struct nlattr *tb[])
|
struct nlattr *tb[])
|
||||||
{
|
{
|
||||||
|
@ -831,6 +881,12 @@ static int process_event(struct nl_msg *msg, void *arg)
|
||||||
case NL80211_CMD_JOIN_IBSS:
|
case NL80211_CMD_JOIN_IBSS:
|
||||||
mlme_event_join_ibss(drv, tb);
|
mlme_event_join_ibss(drv, tb);
|
||||||
break;
|
break;
|
||||||
|
case NL80211_CMD_REMAIN_ON_CHANNEL:
|
||||||
|
mlme_event_remain_on_channel(drv, 0, tb);
|
||||||
|
break;
|
||||||
|
case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
|
||||||
|
mlme_event_remain_on_channel(drv, 1, tb);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
|
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
|
||||||
"(cmd=%d)", gnlh->cmd);
|
"(cmd=%d)", gnlh->cmd);
|
||||||
|
@ -4415,6 +4471,90 @@ static int wpa_driver_nl80211_if_remove(void *priv,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int cookie_handler(struct nl_msg *msg, void *arg)
|
||||||
|
{
|
||||||
|
struct nlattr *tb[NL80211_ATTR_MAX + 1];
|
||||||
|
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
|
||||||
|
u64 *cookie = arg;
|
||||||
|
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||||
|
genlmsg_attrlen(gnlh, 0), NULL);
|
||||||
|
if (tb[NL80211_ATTR_COOKIE])
|
||||||
|
*cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
|
||||||
|
return NL_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
|
||||||
|
unsigned int duration)
|
||||||
|
{
|
||||||
|
struct wpa_driver_nl80211_data *drv = priv;
|
||||||
|
struct nl_msg *msg;
|
||||||
|
int ret;
|
||||||
|
u64 cookie;
|
||||||
|
|
||||||
|
msg = nlmsg_alloc();
|
||||||
|
if (!msg)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
|
||||||
|
NL80211_CMD_REMAIN_ON_CHANNEL, 0);
|
||||||
|
|
||||||
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
|
||||||
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
|
||||||
|
NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
|
||||||
|
|
||||||
|
cookie = 0;
|
||||||
|
ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
|
||||||
|
if (ret == 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
|
||||||
|
"0x%llx for freq=%u MHz duration=%u",
|
||||||
|
(long long unsigned int) cookie, freq, duration);
|
||||||
|
drv->remain_on_chan_cookie = cookie;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
|
||||||
|
"(freq=%d): %d (%s)", freq, ret, strerror(-ret));
|
||||||
|
nla_put_failure:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
|
||||||
|
{
|
||||||
|
struct wpa_driver_nl80211_data *drv = priv;
|
||||||
|
struct nl_msg *msg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!drv->pending_remain_on_chan) {
|
||||||
|
wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
|
||||||
|
"to cancel");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
|
||||||
|
"0x%llx",
|
||||||
|
(long long unsigned int) drv->remain_on_chan_cookie);
|
||||||
|
|
||||||
|
msg = nlmsg_alloc();
|
||||||
|
if (!msg)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
|
||||||
|
NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0);
|
||||||
|
|
||||||
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
|
||||||
|
NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
|
||||||
|
|
||||||
|
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
|
||||||
|
if (ret == 0)
|
||||||
|
return 0;
|
||||||
|
wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
|
||||||
|
"%d (%s)", ret, strerror(-ret));
|
||||||
|
nla_put_failure:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
|
static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
|
||||||
void *timeout_ctx)
|
void *timeout_ctx)
|
||||||
{
|
{
|
||||||
|
@ -4543,6 +4683,9 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
|
||||||
.set_sta_vlan = i802_set_sta_vlan,
|
.set_sta_vlan = i802_set_sta_vlan,
|
||||||
.set_wds_sta = i802_set_wds_sta,
|
.set_wds_sta = i802_set_wds_sta,
|
||||||
#endif /* HOSTAPD */
|
#endif /* HOSTAPD */
|
||||||
|
.remain_on_channel = wpa_driver_nl80211_remain_on_channel,
|
||||||
|
.cancel_remain_on_channel =
|
||||||
|
wpa_driver_nl80211_cancel_remain_on_channel,
|
||||||
.probe_req_report = wpa_driver_nl80211_probe_req_report,
|
.probe_req_report = wpa_driver_nl80211_probe_req_report,
|
||||||
.alloc_interface_addr = wpa_driver_nl80211_alloc_interface_addr,
|
.alloc_interface_addr = wpa_driver_nl80211_alloc_interface_addr,
|
||||||
.release_interface_addr = wpa_driver_nl80211_release_interface_addr,
|
.release_interface_addr = wpa_driver_nl80211_release_interface_addr,
|
||||||
|
|
|
@ -104,6 +104,10 @@ struct wpa_driver_test_data {
|
||||||
int alloc_iface_idx;
|
int alloc_iface_idx;
|
||||||
|
|
||||||
int probe_req_report;
|
int probe_req_report;
|
||||||
|
unsigned int remain_on_channel_freq;
|
||||||
|
unsigned int remain_on_channel_duration;
|
||||||
|
|
||||||
|
int current_freq;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,6 +116,7 @@ static int wpa_driver_test_attach(struct wpa_driver_test_data *drv,
|
||||||
const char *dir, int ap);
|
const char *dir, int ap);
|
||||||
static void wpa_driver_test_close_test_socket(
|
static void wpa_driver_test_close_test_socket(
|
||||||
struct wpa_driver_test_data *drv);
|
struct wpa_driver_test_data *drv);
|
||||||
|
static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx);
|
||||||
|
|
||||||
|
|
||||||
static void test_driver_free_bss(struct test_driver_bss *bss)
|
static void test_driver_free_bss(struct test_driver_bss *bss)
|
||||||
|
@ -1986,6 +1991,7 @@ static void wpa_driver_test_deinit(void *priv)
|
||||||
wpa_driver_test_close_test_socket(drv);
|
wpa_driver_test_close_test_socket(drv);
|
||||||
eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx);
|
eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx);
|
||||||
eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL);
|
eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL);
|
||||||
|
eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
|
||||||
os_free(drv->test_dir);
|
os_free(drv->test_dir);
|
||||||
for (i = 0; i < MAX_SCAN_RESULTS; i++)
|
for (i = 0; i < MAX_SCAN_RESULTS; i++)
|
||||||
os_free(drv->scanres[i]);
|
os_free(drv->scanres[i]);
|
||||||
|
@ -2300,8 +2306,10 @@ static int wpa_driver_test_set_channel(void *priv,
|
||||||
enum hostapd_hw_mode phymode,
|
enum hostapd_hw_mode phymode,
|
||||||
int chan, int freq)
|
int chan, int freq)
|
||||||
{
|
{
|
||||||
|
struct wpa_driver_test_data *drv = priv;
|
||||||
wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d",
|
wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d",
|
||||||
__func__, phymode, chan, freq);
|
__func__, phymode, chan, freq);
|
||||||
|
drv->current_freq = freq;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2464,6 +2472,56 @@ fail:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_driver_test_set_freq(void *priv,
|
||||||
|
struct hostapd_freq_params *freq)
|
||||||
|
{
|
||||||
|
struct wpa_driver_test_data *drv = priv;
|
||||||
|
wpa_printf(MSG_DEBUG, "test: set_freq %u MHz", freq->freq);
|
||||||
|
drv->current_freq = freq->freq;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_driver_test_send_action(void *priv, unsigned int freq,
|
||||||
|
const u8 *dst, const u8 *src,
|
||||||
|
const u8 *data, size_t data_len)
|
||||||
|
{
|
||||||
|
struct wpa_driver_test_data *drv = priv;
|
||||||
|
int ret = -1;
|
||||||
|
u8 *buf;
|
||||||
|
struct ieee80211_hdr *hdr;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "test: Send Action frame");
|
||||||
|
|
||||||
|
if ((drv->remain_on_channel_freq &&
|
||||||
|
freq != drv->remain_on_channel_freq) ||
|
||||||
|
(drv->remain_on_channel_freq == 0 &&
|
||||||
|
freq != (unsigned int) drv->current_freq)) {
|
||||||
|
wpa_printf(MSG_DEBUG, "test: Reject Action frame TX on "
|
||||||
|
"unexpected channel: freq=%u MHz (current_freq=%u "
|
||||||
|
"MHz, remain-on-channel freq=%u MHz)",
|
||||||
|
freq, drv->current_freq,
|
||||||
|
drv->remain_on_channel_freq);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = os_zalloc(24 + data_len);
|
||||||
|
if (buf == NULL)
|
||||||
|
return ret;
|
||||||
|
os_memcpy(buf + 24, data, data_len);
|
||||||
|
hdr = (struct ieee80211_hdr *) buf;
|
||||||
|
hdr->frame_control =
|
||||||
|
IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
|
||||||
|
os_memcpy(hdr->addr1, dst, ETH_ALEN);
|
||||||
|
os_memcpy(hdr->addr2, src, ETH_ALEN);
|
||||||
|
os_memcpy(hdr->addr3, "\xff\xff\xff\xff\xff\xff", ETH_ALEN);
|
||||||
|
|
||||||
|
ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len);
|
||||||
|
os_free(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int wpa_driver_test_alloc_interface_addr(void *priv, u8 *addr)
|
static int wpa_driver_test_alloc_interface_addr(void *priv, u8 *addr)
|
||||||
{
|
{
|
||||||
struct wpa_driver_test_data *drv = priv;
|
struct wpa_driver_test_data *drv = priv;
|
||||||
|
@ -2482,6 +2540,64 @@ static void wpa_driver_test_release_interface_addr(void *priv, const u8 *addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx)
|
||||||
|
{
|
||||||
|
struct wpa_driver_test_data *drv = eloop_ctx;
|
||||||
|
union wpa_event_data data;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "test: Remain-on-channel timeout");
|
||||||
|
|
||||||
|
os_memset(&data, 0, sizeof(data));
|
||||||
|
data.remain_on_channel.freq = drv->remain_on_channel_freq;
|
||||||
|
data.remain_on_channel.duration = drv->remain_on_channel_duration;
|
||||||
|
wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
|
||||||
|
|
||||||
|
drv->remain_on_channel_freq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq,
|
||||||
|
unsigned int duration)
|
||||||
|
{
|
||||||
|
struct wpa_driver_test_data *drv = priv;
|
||||||
|
union wpa_event_data data;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "%s(freq=%u, duration=%u)",
|
||||||
|
__func__, freq, duration);
|
||||||
|
if (drv->remain_on_channel_freq &&
|
||||||
|
drv->remain_on_channel_freq != freq) {
|
||||||
|
wpa_printf(MSG_DEBUG, "test: Refuse concurrent "
|
||||||
|
"remain_on_channel request");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv->remain_on_channel_freq = freq;
|
||||||
|
drv->remain_on_channel_duration = duration;
|
||||||
|
eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
|
||||||
|
eloop_register_timeout(duration / 1000, (duration % 1000) * 1000,
|
||||||
|
test_remain_on_channel_timeout, drv, NULL);
|
||||||
|
|
||||||
|
os_memset(&data, 0, sizeof(data));
|
||||||
|
data.remain_on_channel.freq = freq;
|
||||||
|
data.remain_on_channel.duration = duration;
|
||||||
|
wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_driver_test_cancel_remain_on_channel(void *priv)
|
||||||
|
{
|
||||||
|
struct wpa_driver_test_data *drv = priv;
|
||||||
|
wpa_printf(MSG_DEBUG, "%s", __func__);
|
||||||
|
if (!drv->remain_on_channel_freq)
|
||||||
|
return -1;
|
||||||
|
drv->remain_on_channel_freq = 0;
|
||||||
|
eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int wpa_driver_test_probe_req_report(void *priv, int report)
|
static int wpa_driver_test_probe_req_report(void *priv, int report)
|
||||||
{
|
{
|
||||||
struct wpa_driver_test_data *drv = priv;
|
struct wpa_driver_test_data *drv = priv;
|
||||||
|
@ -2534,7 +2650,11 @@ const struct wpa_driver_ops wpa_driver_test_ops = {
|
||||||
.init2 = wpa_driver_test_init2,
|
.init2 = wpa_driver_test_init2,
|
||||||
.get_interfaces = wpa_driver_test_get_interfaces,
|
.get_interfaces = wpa_driver_test_get_interfaces,
|
||||||
.scan2 = wpa_driver_test_scan,
|
.scan2 = wpa_driver_test_scan,
|
||||||
|
.set_freq = wpa_driver_test_set_freq,
|
||||||
|
.send_action = wpa_driver_test_send_action,
|
||||||
.alloc_interface_addr = wpa_driver_test_alloc_interface_addr,
|
.alloc_interface_addr = wpa_driver_test_alloc_interface_addr,
|
||||||
.release_interface_addr = wpa_driver_test_release_interface_addr,
|
.release_interface_addr = wpa_driver_test_release_interface_addr,
|
||||||
|
.remain_on_channel = wpa_driver_test_remain_on_channel,
|
||||||
|
.cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel,
|
||||||
.probe_req_report = wpa_driver_test_probe_req_report,
|
.probe_req_report = wpa_driver_test_probe_req_report,
|
||||||
};
|
};
|
||||||
|
|
|
@ -383,6 +383,17 @@ static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
|
||||||
|
unsigned int freq,
|
||||||
|
const u8 *dst, const u8 *src,
|
||||||
|
const u8 *data, size_t data_len)
|
||||||
|
{
|
||||||
|
if (wpa_s->driver->send_action)
|
||||||
|
return wpa_s->driver->send_action(wpa_s->drv_priv, freq,
|
||||||
|
dst, src, data, data_len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int wpa_drv_alloc_interface_addr(struct wpa_supplicant *wpa_s,
|
static inline int wpa_drv_alloc_interface_addr(struct wpa_supplicant *wpa_s,
|
||||||
u8 *addr)
|
u8 *addr)
|
||||||
{
|
{
|
||||||
|
@ -399,6 +410,25 @@ static inline void wpa_drv_release_interface_addr(struct wpa_supplicant *wpa_s,
|
||||||
wpa_s->driver->release_interface_addr(wpa_s->drv_priv, addr);
|
wpa_s->driver->release_interface_addr(wpa_s->drv_priv, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int wpa_drv_remain_on_channel(struct wpa_supplicant *wpa_s,
|
||||||
|
unsigned int freq,
|
||||||
|
unsigned int duration)
|
||||||
|
{
|
||||||
|
if (wpa_s->driver->remain_on_channel)
|
||||||
|
return wpa_s->driver->remain_on_channel(wpa_s->drv_priv, freq,
|
||||||
|
duration);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int wpa_drv_cancel_remain_on_channel(
|
||||||
|
struct wpa_supplicant *wpa_s)
|
||||||
|
{
|
||||||
|
if (wpa_s->driver->cancel_remain_on_channel)
|
||||||
|
return wpa_s->driver->cancel_remain_on_channel(
|
||||||
|
wpa_s->drv_priv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s,
|
static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s,
|
||||||
int report)
|
int report)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue