1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * wpa_supplicant - Off-channel Action frame TX/RX 3e5b75505Sopenharmony_ci * Copyright (c) 2009-2010, Atheros Communications 4e5b75505Sopenharmony_ci * Copyright (c) 2011, Qualcomm Atheros 5e5b75505Sopenharmony_ci * 6e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 7e5b75505Sopenharmony_ci * See README for more details. 8e5b75505Sopenharmony_ci */ 9e5b75505Sopenharmony_ci 10e5b75505Sopenharmony_ci#include "includes.h" 11e5b75505Sopenharmony_ci 12e5b75505Sopenharmony_ci#include "common.h" 13e5b75505Sopenharmony_ci#include "utils/eloop.h" 14e5b75505Sopenharmony_ci#include "wpa_supplicant_i.h" 15e5b75505Sopenharmony_ci#include "p2p_supplicant.h" 16e5b75505Sopenharmony_ci#include "driver_i.h" 17e5b75505Sopenharmony_ci#include "offchannel.h" 18e5b75505Sopenharmony_ci 19e5b75505Sopenharmony_ci 20e5b75505Sopenharmony_ci 21e5b75505Sopenharmony_cistatic struct wpa_supplicant * 22e5b75505Sopenharmony_ciwpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src) 23e5b75505Sopenharmony_ci{ 24e5b75505Sopenharmony_ci struct wpa_supplicant *iface; 25e5b75505Sopenharmony_ci 26e5b75505Sopenharmony_ci if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0) { 27e5b75505Sopenharmony_ci#ifdef CONFIG_P2P 28e5b75505Sopenharmony_ci if (wpa_s->p2p_mgmt && wpa_s != wpa_s->parent && 29e5b75505Sopenharmony_ci wpa_s->parent->ap_iface && 30e5b75505Sopenharmony_ci os_memcmp(wpa_s->parent->own_addr, 31e5b75505Sopenharmony_ci wpa_s->own_addr, ETH_ALEN) == 0 && 32e5b75505Sopenharmony_ci wpabuf_len(wpa_s->pending_action_tx) >= 2 && 33e5b75505Sopenharmony_ci *wpabuf_head_u8(wpa_s->pending_action_tx) != 34e5b75505Sopenharmony_ci WLAN_ACTION_PUBLIC) { 35e5b75505Sopenharmony_ci /* 36e5b75505Sopenharmony_ci * When P2P Device interface has same MAC address as 37e5b75505Sopenharmony_ci * the GO interface, make sure non-Public Action frames 38e5b75505Sopenharmony_ci * are sent through the GO interface. The P2P Device 39e5b75505Sopenharmony_ci * interface can only send Public Action frames. 40e5b75505Sopenharmony_ci */ 41e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 42e5b75505Sopenharmony_ci "P2P: Use GO interface %s instead of interface %s for Action TX", 43e5b75505Sopenharmony_ci wpa_s->parent->ifname, wpa_s->ifname); 44e5b75505Sopenharmony_ci return wpa_s->parent; 45e5b75505Sopenharmony_ci } 46e5b75505Sopenharmony_ci#endif /* CONFIG_P2P */ 47e5b75505Sopenharmony_ci return wpa_s; 48e5b75505Sopenharmony_ci } 49e5b75505Sopenharmony_ci 50e5b75505Sopenharmony_ci /* 51e5b75505Sopenharmony_ci * Try to find a group interface that matches with the source address. 52e5b75505Sopenharmony_ci */ 53e5b75505Sopenharmony_ci iface = wpa_s->global->ifaces; 54e5b75505Sopenharmony_ci while (iface) { 55e5b75505Sopenharmony_ci if (os_memcmp(src, iface->own_addr, ETH_ALEN) == 0) 56e5b75505Sopenharmony_ci break; 57e5b75505Sopenharmony_ci iface = iface->next; 58e5b75505Sopenharmony_ci } 59e5b75505Sopenharmony_ci if (iface) { 60e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "P2P: Use group interface %s " 61e5b75505Sopenharmony_ci "instead of interface %s for Action TX", 62e5b75505Sopenharmony_ci iface->ifname, wpa_s->ifname); 63e5b75505Sopenharmony_ci return iface; 64e5b75505Sopenharmony_ci } 65e5b75505Sopenharmony_ci 66e5b75505Sopenharmony_ci return wpa_s; 67e5b75505Sopenharmony_ci} 68e5b75505Sopenharmony_ci 69e5b75505Sopenharmony_ci 70e5b75505Sopenharmony_cistatic void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) 71e5b75505Sopenharmony_ci{ 72e5b75505Sopenharmony_ci struct wpa_supplicant *wpa_s = eloop_ctx; 73e5b75505Sopenharmony_ci struct wpa_supplicant *iface; 74e5b75505Sopenharmony_ci int res; 75e5b75505Sopenharmony_ci int without_roc; 76e5b75505Sopenharmony_ci 77e5b75505Sopenharmony_ci without_roc = wpa_s->pending_action_without_roc; 78e5b75505Sopenharmony_ci wpa_s->pending_action_without_roc = 0; 79e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 80e5b75505Sopenharmony_ci "Off-channel: Send Action callback (without_roc=%d pending_action_tx=%p pending_action_tx_done=%d)", 81e5b75505Sopenharmony_ci without_roc, wpa_s->pending_action_tx, 82e5b75505Sopenharmony_ci !!wpa_s->pending_action_tx_done); 83e5b75505Sopenharmony_ci 84e5b75505Sopenharmony_ci if (wpa_s->pending_action_tx == NULL || wpa_s->pending_action_tx_done) 85e5b75505Sopenharmony_ci return; 86e5b75505Sopenharmony_ci 87e5b75505Sopenharmony_ci /* 88e5b75505Sopenharmony_ci * This call is likely going to be on the P2P device instance if the 89e5b75505Sopenharmony_ci * driver uses a separate interface for that purpose. However, some 90e5b75505Sopenharmony_ci * Action frames are actually sent within a P2P Group and when that is 91e5b75505Sopenharmony_ci * the case, we need to follow power saving (e.g., GO buffering the 92e5b75505Sopenharmony_ci * frame for a client in PS mode or a client following the advertised 93e5b75505Sopenharmony_ci * NoA from its GO). To make that easier for the driver, select the 94e5b75505Sopenharmony_ci * correct group interface here. 95e5b75505Sopenharmony_ci */ 96e5b75505Sopenharmony_ci iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src); 97e5b75505Sopenharmony_ci 98e5b75505Sopenharmony_ci if (wpa_s->off_channel_freq != wpa_s->pending_action_freq && 99e5b75505Sopenharmony_ci wpa_s->pending_action_freq != 0 && 100e5b75505Sopenharmony_ci wpa_s->pending_action_freq != iface->assoc_freq) { 101e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Pending Action frame TX " 102e5b75505Sopenharmony_ci "waiting for another freq=%u (off_channel_freq=%u " 103e5b75505Sopenharmony_ci "assoc_freq=%u)", 104e5b75505Sopenharmony_ci wpa_s->pending_action_freq, 105e5b75505Sopenharmony_ci wpa_s->off_channel_freq, 106e5b75505Sopenharmony_ci iface->assoc_freq); 107e5b75505Sopenharmony_ci if (without_roc && wpa_s->off_channel_freq == 0) { 108e5b75505Sopenharmony_ci unsigned int duration = 200; 109e5b75505Sopenharmony_ci /* 110e5b75505Sopenharmony_ci * We may get here if wpas_send_action() found us to be 111e5b75505Sopenharmony_ci * on the correct channel, but remain-on-channel cancel 112e5b75505Sopenharmony_ci * event was received before getting here. 113e5b75505Sopenharmony_ci */ 114e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Schedule " 115e5b75505Sopenharmony_ci "remain-on-channel to send Action frame"); 116e5b75505Sopenharmony_ci#ifdef CONFIG_TESTING_OPTIONS 117e5b75505Sopenharmony_ci if (wpa_s->extra_roc_dur) { 118e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 119e5b75505Sopenharmony_ci "TESTING: Increase ROC duration %u -> %u", 120e5b75505Sopenharmony_ci duration, 121e5b75505Sopenharmony_ci duration + wpa_s->extra_roc_dur); 122e5b75505Sopenharmony_ci duration += wpa_s->extra_roc_dur; 123e5b75505Sopenharmony_ci } 124e5b75505Sopenharmony_ci#endif /* CONFIG_TESTING_OPTIONS */ 125e5b75505Sopenharmony_ci if (wpa_drv_remain_on_channel( 126e5b75505Sopenharmony_ci wpa_s, wpa_s->pending_action_freq, 127e5b75505Sopenharmony_ci duration) < 0) { 128e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Failed to " 129e5b75505Sopenharmony_ci "request driver to remain on " 130e5b75505Sopenharmony_ci "channel (%u MHz) for Action Frame " 131e5b75505Sopenharmony_ci "TX", wpa_s->pending_action_freq); 132e5b75505Sopenharmony_ci } else { 133e5b75505Sopenharmony_ci wpa_s->off_channel_freq = 0; 134e5b75505Sopenharmony_ci wpa_s->roc_waiting_drv_freq = 135e5b75505Sopenharmony_ci wpa_s->pending_action_freq; 136e5b75505Sopenharmony_ci } 137e5b75505Sopenharmony_ci } 138e5b75505Sopenharmony_ci return; 139e5b75505Sopenharmony_ci } 140e5b75505Sopenharmony_ci 141e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to " 142e5b75505Sopenharmony_ci MACSTR " using interface %s (pending_action_tx=%p)", 143e5b75505Sopenharmony_ci MAC2STR(wpa_s->pending_action_dst), iface->ifname, 144e5b75505Sopenharmony_ci wpa_s->pending_action_tx); 145e5b75505Sopenharmony_ci res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0, 146e5b75505Sopenharmony_ci wpa_s->pending_action_dst, 147e5b75505Sopenharmony_ci wpa_s->pending_action_src, 148e5b75505Sopenharmony_ci wpa_s->pending_action_bssid, 149e5b75505Sopenharmony_ci wpabuf_head(wpa_s->pending_action_tx), 150e5b75505Sopenharmony_ci wpabuf_len(wpa_s->pending_action_tx), 151e5b75505Sopenharmony_ci wpa_s->pending_action_no_cck); 152e5b75505Sopenharmony_ci if (res) { 153e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Failed to send the " 154e5b75505Sopenharmony_ci "pending Action frame"); 155e5b75505Sopenharmony_ci /* 156e5b75505Sopenharmony_ci * Use fake TX status event to allow state machines to 157e5b75505Sopenharmony_ci * continue. 158e5b75505Sopenharmony_ci */ 159e5b75505Sopenharmony_ci offchannel_send_action_tx_status( 160e5b75505Sopenharmony_ci wpa_s, wpa_s->pending_action_dst, 161e5b75505Sopenharmony_ci wpabuf_head(wpa_s->pending_action_tx), 162e5b75505Sopenharmony_ci wpabuf_len(wpa_s->pending_action_tx), 163e5b75505Sopenharmony_ci OFFCHANNEL_SEND_ACTION_FAILED); 164e5b75505Sopenharmony_ci } 165e5b75505Sopenharmony_ci} 166e5b75505Sopenharmony_ci 167e5b75505Sopenharmony_ci 168e5b75505Sopenharmony_ci/** 169e5b75505Sopenharmony_ci * offchannel_send_action_tx_status - TX status callback 170e5b75505Sopenharmony_ci * @wpa_s: Pointer to wpa_supplicant data 171e5b75505Sopenharmony_ci * @dst: Destination MAC address of the transmitted Action frame 172e5b75505Sopenharmony_ci * @data: Transmitted frame payload 173e5b75505Sopenharmony_ci * @data_len: Length of @data in bytes 174e5b75505Sopenharmony_ci * @result: TX status 175e5b75505Sopenharmony_ci * 176e5b75505Sopenharmony_ci * This function is called whenever the driver indicates a TX status event for 177e5b75505Sopenharmony_ci * a frame sent by offchannel_send_action() using wpa_drv_send_action(). 178e5b75505Sopenharmony_ci */ 179e5b75505Sopenharmony_civoid offchannel_send_action_tx_status( 180e5b75505Sopenharmony_ci struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data, 181e5b75505Sopenharmony_ci size_t data_len, enum offchannel_send_action_result result) 182e5b75505Sopenharmony_ci{ 183e5b75505Sopenharmony_ci if (wpa_s->pending_action_tx == NULL) { 184e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - " 185e5b75505Sopenharmony_ci "no pending operation"); 186e5b75505Sopenharmony_ci return; 187e5b75505Sopenharmony_ci } 188e5b75505Sopenharmony_ci 189e5b75505Sopenharmony_ci if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) { 190e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - " 191e5b75505Sopenharmony_ci "unknown destination address"); 192e5b75505Sopenharmony_ci return; 193e5b75505Sopenharmony_ci } 194e5b75505Sopenharmony_ci 195e5b75505Sopenharmony_ci /* Accept report only if the contents of the frame matches */ 196e5b75505Sopenharmony_ci if (data_len - wpabuf_len(wpa_s->pending_action_tx) != 24 || 197e5b75505Sopenharmony_ci os_memcmp(data + 24, wpabuf_head(wpa_s->pending_action_tx), 198e5b75505Sopenharmony_ci wpabuf_len(wpa_s->pending_action_tx)) != 0) { 199e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - " 200e5b75505Sopenharmony_ci "mismatching contents with pending frame"); 201e5b75505Sopenharmony_ci wpa_hexdump(MSG_MSGDUMP, "TX status frame data", 202e5b75505Sopenharmony_ci data, data_len); 203e5b75505Sopenharmony_ci wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", 204e5b75505Sopenharmony_ci wpa_s->pending_action_tx); 205e5b75505Sopenharmony_ci return; 206e5b75505Sopenharmony_ci } 207e5b75505Sopenharmony_ci 208e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 209e5b75505Sopenharmony_ci "Off-channel: Delete matching pending action frame (dst=" 210e5b75505Sopenharmony_ci MACSTR " pending_action_tx=%p)", MAC2STR(dst), 211e5b75505Sopenharmony_ci wpa_s->pending_action_tx); 212e5b75505Sopenharmony_ci wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", 213e5b75505Sopenharmony_ci wpa_s->pending_action_tx); 214e5b75505Sopenharmony_ci wpabuf_free(wpa_s->pending_action_tx); 215e5b75505Sopenharmony_ci wpa_s->pending_action_tx = NULL; 216e5b75505Sopenharmony_ci 217e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: TX status result=%d cb=%p", 218e5b75505Sopenharmony_ci result, wpa_s->pending_action_tx_status_cb); 219e5b75505Sopenharmony_ci 220e5b75505Sopenharmony_ci if (wpa_s->pending_action_tx_status_cb) { 221e5b75505Sopenharmony_ci wpa_s->pending_action_tx_status_cb( 222e5b75505Sopenharmony_ci wpa_s, wpa_s->pending_action_freq, 223e5b75505Sopenharmony_ci wpa_s->pending_action_dst, wpa_s->pending_action_src, 224e5b75505Sopenharmony_ci wpa_s->pending_action_bssid, 225e5b75505Sopenharmony_ci data, data_len, result); 226e5b75505Sopenharmony_ci } 227e5b75505Sopenharmony_ci 228e5b75505Sopenharmony_ci#ifdef CONFIG_P2P 229e5b75505Sopenharmony_ci if (wpa_s->p2p_long_listen > 0) { 230e5b75505Sopenharmony_ci /* Continue the listen */ 231e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state"); 232e5b75505Sopenharmony_ci wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen); 233e5b75505Sopenharmony_ci } 234e5b75505Sopenharmony_ci#endif /* CONFIG_P2P */ 235e5b75505Sopenharmony_ci} 236e5b75505Sopenharmony_ci 237e5b75505Sopenharmony_ci 238e5b75505Sopenharmony_ci/** 239e5b75505Sopenharmony_ci * offchannel_send_action - Request off-channel Action frame TX 240e5b75505Sopenharmony_ci * @wpa_s: Pointer to wpa_supplicant data 241e5b75505Sopenharmony_ci * @freq: The frequency in MHz indicating the channel on which the frame is to 242e5b75505Sopenharmony_ci * transmitted or 0 for the current channel (only if associated) 243e5b75505Sopenharmony_ci * @dst: Action frame destination MAC address 244e5b75505Sopenharmony_ci * @src: Action frame source MAC address 245e5b75505Sopenharmony_ci * @bssid: Action frame BSSID 246e5b75505Sopenharmony_ci * @buf: Frame to transmit starting from the Category field 247e5b75505Sopenharmony_ci * @len: Length of @buf in bytes 248e5b75505Sopenharmony_ci * @wait_time: Wait time for response in milliseconds 249e5b75505Sopenharmony_ci * @tx_cb: Callback function for indicating TX status or %NULL for now callback 250e5b75505Sopenharmony_ci * @no_cck: Whether CCK rates are to be disallowed for TX rate selection 251e5b75505Sopenharmony_ci * Returns: 0 on success or -1 on failure 252e5b75505Sopenharmony_ci * 253e5b75505Sopenharmony_ci * This function is used to request an Action frame to be transmitted on the 254e5b75505Sopenharmony_ci * current operating channel or on another channel (off-channel). The actual 255e5b75505Sopenharmony_ci * frame transmission will be delayed until the driver is ready on the specified 256e5b75505Sopenharmony_ci * channel. The @wait_time parameter can be used to request the driver to remain 257e5b75505Sopenharmony_ci * awake on the channel to wait for a response. 258e5b75505Sopenharmony_ci */ 259e5b75505Sopenharmony_ciint offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, 260e5b75505Sopenharmony_ci const u8 *dst, const u8 *src, const u8 *bssid, 261e5b75505Sopenharmony_ci const u8 *buf, size_t len, unsigned int wait_time, 262e5b75505Sopenharmony_ci void (*tx_cb)(struct wpa_supplicant *wpa_s, 263e5b75505Sopenharmony_ci unsigned int freq, const u8 *dst, 264e5b75505Sopenharmony_ci const u8 *src, const u8 *bssid, 265e5b75505Sopenharmony_ci const u8 *data, size_t data_len, 266e5b75505Sopenharmony_ci enum offchannel_send_action_result 267e5b75505Sopenharmony_ci result), 268e5b75505Sopenharmony_ci int no_cck) 269e5b75505Sopenharmony_ci{ 270e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Send action frame: freq=%d dst=" 271e5b75505Sopenharmony_ci MACSTR " src=" MACSTR " bssid=" MACSTR " len=%d", 272e5b75505Sopenharmony_ci freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), 273e5b75505Sopenharmony_ci (int) len); 274e5b75505Sopenharmony_ci 275e5b75505Sopenharmony_ci wpa_s->pending_action_tx_status_cb = tx_cb; 276e5b75505Sopenharmony_ci 277e5b75505Sopenharmony_ci if (wpa_s->pending_action_tx) { 278e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action " 279e5b75505Sopenharmony_ci "frame TX to " MACSTR " (pending_action_tx=%p)", 280e5b75505Sopenharmony_ci MAC2STR(wpa_s->pending_action_dst), 281e5b75505Sopenharmony_ci wpa_s->pending_action_tx); 282e5b75505Sopenharmony_ci wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", 283e5b75505Sopenharmony_ci wpa_s->pending_action_tx); 284e5b75505Sopenharmony_ci wpabuf_free(wpa_s->pending_action_tx); 285e5b75505Sopenharmony_ci } 286e5b75505Sopenharmony_ci wpa_s->pending_action_tx_done = 0; 287e5b75505Sopenharmony_ci wpa_s->pending_action_tx = wpabuf_alloc(len); 288e5b75505Sopenharmony_ci if (wpa_s->pending_action_tx == NULL) { 289e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action " 290e5b75505Sopenharmony_ci "frame TX buffer (len=%llu)", 291e5b75505Sopenharmony_ci (unsigned long long) len); 292e5b75505Sopenharmony_ci return -1; 293e5b75505Sopenharmony_ci } 294e5b75505Sopenharmony_ci wpabuf_put_data(wpa_s->pending_action_tx, buf, len); 295e5b75505Sopenharmony_ci os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN); 296e5b75505Sopenharmony_ci os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN); 297e5b75505Sopenharmony_ci os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN); 298e5b75505Sopenharmony_ci wpa_s->pending_action_freq = freq; 299e5b75505Sopenharmony_ci wpa_s->pending_action_no_cck = no_cck; 300e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 301e5b75505Sopenharmony_ci "Off-channel: Stored pending action frame (dst=" MACSTR 302e5b75505Sopenharmony_ci " pending_action_tx=%p)", 303e5b75505Sopenharmony_ci MAC2STR(dst), wpa_s->pending_action_tx); 304e5b75505Sopenharmony_ci wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", 305e5b75505Sopenharmony_ci wpa_s->pending_action_tx); 306e5b75505Sopenharmony_ci 307e5b75505Sopenharmony_ci if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) { 308e5b75505Sopenharmony_ci struct wpa_supplicant *iface; 309e5b75505Sopenharmony_ci int ret; 310e5b75505Sopenharmony_ci 311e5b75505Sopenharmony_ci iface = wpas_get_tx_interface(wpa_s, src); 312e5b75505Sopenharmony_ci wpa_s->action_tx_wait_time = wait_time; 313e5b75505Sopenharmony_ci if (wait_time) 314e5b75505Sopenharmony_ci wpa_s->action_tx_wait_time_used = 1; 315e5b75505Sopenharmony_ci 316e5b75505Sopenharmony_ci ret = wpa_drv_send_action( 317e5b75505Sopenharmony_ci iface, wpa_s->pending_action_freq, 318e5b75505Sopenharmony_ci wait_time, wpa_s->pending_action_dst, 319e5b75505Sopenharmony_ci wpa_s->pending_action_src, wpa_s->pending_action_bssid, 320e5b75505Sopenharmony_ci wpabuf_head(wpa_s->pending_action_tx), 321e5b75505Sopenharmony_ci wpabuf_len(wpa_s->pending_action_tx), 322e5b75505Sopenharmony_ci wpa_s->pending_action_no_cck); 323e5b75505Sopenharmony_ci if (ret == 0) 324e5b75505Sopenharmony_ci wpa_s->pending_action_tx_done = 1; 325e5b75505Sopenharmony_ci return ret; 326e5b75505Sopenharmony_ci } 327e5b75505Sopenharmony_ci 328e5b75505Sopenharmony_ci if (freq) { 329e5b75505Sopenharmony_ci struct wpa_supplicant *tx_iface; 330e5b75505Sopenharmony_ci tx_iface = wpas_get_tx_interface(wpa_s, src); 331e5b75505Sopenharmony_ci if (tx_iface->assoc_freq == freq) { 332e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Already on " 333e5b75505Sopenharmony_ci "requested channel (TX interface operating " 334e5b75505Sopenharmony_ci "channel)"); 335e5b75505Sopenharmony_ci freq = 0; 336e5b75505Sopenharmony_ci } 337e5b75505Sopenharmony_ci } 338e5b75505Sopenharmony_ci 339e5b75505Sopenharmony_ci if (wpa_s->off_channel_freq == freq || freq == 0) { 340e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Already on requested " 341e5b75505Sopenharmony_ci "channel; send Action frame immediately"); 342e5b75505Sopenharmony_ci /* TODO: Would there ever be need to extend the current 343e5b75505Sopenharmony_ci * duration on the channel? */ 344e5b75505Sopenharmony_ci wpa_s->pending_action_without_roc = 1; 345e5b75505Sopenharmony_ci eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL); 346e5b75505Sopenharmony_ci eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL); 347e5b75505Sopenharmony_ci return 0; 348e5b75505Sopenharmony_ci } 349e5b75505Sopenharmony_ci wpa_s->pending_action_without_roc = 0; 350e5b75505Sopenharmony_ci 351e5b75505Sopenharmony_ci if (wpa_s->roc_waiting_drv_freq == freq) { 352e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Already waiting for " 353e5b75505Sopenharmony_ci "driver to get to frequency %u MHz; continue " 354e5b75505Sopenharmony_ci "waiting to send the Action frame", freq); 355e5b75505Sopenharmony_ci return 0; 356e5b75505Sopenharmony_ci } 357e5b75505Sopenharmony_ci 358e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Schedule Action frame to be " 359e5b75505Sopenharmony_ci "transmitted once the driver gets to the requested " 360e5b75505Sopenharmony_ci "channel"); 361e5b75505Sopenharmony_ci if (wait_time > wpa_s->max_remain_on_chan) 362e5b75505Sopenharmony_ci wait_time = wpa_s->max_remain_on_chan; 363e5b75505Sopenharmony_ci else if (wait_time == 0) 364e5b75505Sopenharmony_ci wait_time = 20; 365e5b75505Sopenharmony_ci#ifdef CONFIG_TESTING_OPTIONS 366e5b75505Sopenharmony_ci if (wpa_s->extra_roc_dur) { 367e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u", 368e5b75505Sopenharmony_ci wait_time, wait_time + wpa_s->extra_roc_dur); 369e5b75505Sopenharmony_ci wait_time += wpa_s->extra_roc_dur; 370e5b75505Sopenharmony_ci } 371e5b75505Sopenharmony_ci#endif /* CONFIG_TESTING_OPTIONS */ 372e5b75505Sopenharmony_ci if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) { 373e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver " 374e5b75505Sopenharmony_ci "to remain on channel (%u MHz) for Action " 375e5b75505Sopenharmony_ci "Frame TX", freq); 376e5b75505Sopenharmony_ci return -1; 377e5b75505Sopenharmony_ci } 378e5b75505Sopenharmony_ci wpa_s->off_channel_freq = 0; 379e5b75505Sopenharmony_ci wpa_s->roc_waiting_drv_freq = freq; 380e5b75505Sopenharmony_ci 381e5b75505Sopenharmony_ci return 0; 382e5b75505Sopenharmony_ci} 383e5b75505Sopenharmony_ci 384e5b75505Sopenharmony_ci 385e5b75505Sopenharmony_ci/** 386e5b75505Sopenharmony_ci * offchannel_send_send_action_done - Notify completion of Action frame sequence 387e5b75505Sopenharmony_ci * @wpa_s: Pointer to wpa_supplicant data 388e5b75505Sopenharmony_ci * 389e5b75505Sopenharmony_ci * This function can be used to cancel a wait for additional response frames on 390e5b75505Sopenharmony_ci * the channel that was used with offchannel_send_action(). 391e5b75505Sopenharmony_ci */ 392e5b75505Sopenharmony_civoid offchannel_send_action_done(struct wpa_supplicant *wpa_s) 393e5b75505Sopenharmony_ci{ 394e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 395e5b75505Sopenharmony_ci "Off-channel: Action frame sequence done notification: pending_action_tx=%p drv_offchan_tx=%d action_tx_wait_time=%d off_channel_freq=%d roc_waiting_drv_freq=%d", 396e5b75505Sopenharmony_ci wpa_s->pending_action_tx, 397e5b75505Sopenharmony_ci !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX), 398e5b75505Sopenharmony_ci wpa_s->action_tx_wait_time, wpa_s->off_channel_freq, 399e5b75505Sopenharmony_ci wpa_s->roc_waiting_drv_freq); 400e5b75505Sopenharmony_ci wpabuf_free(wpa_s->pending_action_tx); 401e5b75505Sopenharmony_ci wpa_s->pending_action_tx = NULL; 402e5b75505Sopenharmony_ci if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX && 403e5b75505Sopenharmony_ci (wpa_s->action_tx_wait_time || wpa_s->action_tx_wait_time_used)) 404e5b75505Sopenharmony_ci wpa_drv_send_action_cancel_wait(wpa_s); 405e5b75505Sopenharmony_ci else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { 406e5b75505Sopenharmony_ci wpa_drv_cancel_remain_on_channel(wpa_s); 407e5b75505Sopenharmony_ci wpa_s->off_channel_freq = 0; 408e5b75505Sopenharmony_ci wpa_s->roc_waiting_drv_freq = 0; 409e5b75505Sopenharmony_ci } 410e5b75505Sopenharmony_ci wpa_s->action_tx_wait_time_used = 0; 411e5b75505Sopenharmony_ci} 412e5b75505Sopenharmony_ci 413e5b75505Sopenharmony_ci 414e5b75505Sopenharmony_ci/** 415e5b75505Sopenharmony_ci * offchannel_remain_on_channel_cb - Remain-on-channel callback function 416e5b75505Sopenharmony_ci * @wpa_s: Pointer to wpa_supplicant data 417e5b75505Sopenharmony_ci * @freq: Frequency (in MHz) of the selected channel 418e5b75505Sopenharmony_ci * @duration: Duration of the remain-on-channel operation in milliseconds 419e5b75505Sopenharmony_ci * 420e5b75505Sopenharmony_ci * This function is called whenever the driver notifies beginning of a 421e5b75505Sopenharmony_ci * remain-on-channel operation. 422e5b75505Sopenharmony_ci */ 423e5b75505Sopenharmony_civoid offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, 424e5b75505Sopenharmony_ci unsigned int freq, unsigned int duration) 425e5b75505Sopenharmony_ci{ 426e5b75505Sopenharmony_ci wpa_s->roc_waiting_drv_freq = 0; 427e5b75505Sopenharmony_ci wpa_s->off_channel_freq = freq; 428e5b75505Sopenharmony_ci wpas_send_action_cb(wpa_s, NULL); 429e5b75505Sopenharmony_ci} 430e5b75505Sopenharmony_ci 431e5b75505Sopenharmony_ci 432e5b75505Sopenharmony_ci/** 433e5b75505Sopenharmony_ci * offchannel_cancel_remain_on_channel_cb - Remain-on-channel stopped callback 434e5b75505Sopenharmony_ci * @wpa_s: Pointer to wpa_supplicant data 435e5b75505Sopenharmony_ci * @freq: Frequency (in MHz) of the selected channel 436e5b75505Sopenharmony_ci * 437e5b75505Sopenharmony_ci * This function is called whenever the driver notifies termination of a 438e5b75505Sopenharmony_ci * remain-on-channel operation. 439e5b75505Sopenharmony_ci */ 440e5b75505Sopenharmony_civoid offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, 441e5b75505Sopenharmony_ci unsigned int freq) 442e5b75505Sopenharmony_ci{ 443e5b75505Sopenharmony_ci wpa_s->off_channel_freq = 0; 444e5b75505Sopenharmony_ci} 445e5b75505Sopenharmony_ci 446e5b75505Sopenharmony_ci 447e5b75505Sopenharmony_ci/** 448e5b75505Sopenharmony_ci * offchannel_pending_action_tx - Check whether there is a pending Action TX 449e5b75505Sopenharmony_ci * @wpa_s: Pointer to wpa_supplicant data 450e5b75505Sopenharmony_ci * Returns: Pointer to pending frame or %NULL if no pending operation 451e5b75505Sopenharmony_ci * 452e5b75505Sopenharmony_ci * This function can be used to check whether there is a pending Action frame TX 453e5b75505Sopenharmony_ci * operation. The returned pointer should be used only for checking whether it 454e5b75505Sopenharmony_ci * is %NULL (no pending frame) or to print the pointer value in debug 455e5b75505Sopenharmony_ci * information (i.e., the pointer should not be dereferenced). 456e5b75505Sopenharmony_ci */ 457e5b75505Sopenharmony_ciconst void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s) 458e5b75505Sopenharmony_ci{ 459e5b75505Sopenharmony_ci return wpa_s->pending_action_tx; 460e5b75505Sopenharmony_ci} 461e5b75505Sopenharmony_ci 462e5b75505Sopenharmony_ci 463e5b75505Sopenharmony_ci/** 464e5b75505Sopenharmony_ci * offchannel_clear_pending_action_tx - Clear pending Action frame TX 465e5b75505Sopenharmony_ci * @wpa_s: Pointer to wpa_supplicant data 466e5b75505Sopenharmony_ci */ 467e5b75505Sopenharmony_civoid offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s) 468e5b75505Sopenharmony_ci{ 469e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 470e5b75505Sopenharmony_ci "Off-channel: Clear pending Action frame TX (pending_action_tx=%p", 471e5b75505Sopenharmony_ci wpa_s->pending_action_tx); 472e5b75505Sopenharmony_ci wpabuf_free(wpa_s->pending_action_tx); 473e5b75505Sopenharmony_ci wpa_s->pending_action_tx = NULL; 474e5b75505Sopenharmony_ci} 475e5b75505Sopenharmony_ci 476e5b75505Sopenharmony_ci 477e5b75505Sopenharmony_ci/** 478e5b75505Sopenharmony_ci * offchannel_deinit - Deinit off-channel operations 479e5b75505Sopenharmony_ci * @wpa_s: Pointer to wpa_supplicant data 480e5b75505Sopenharmony_ci * 481e5b75505Sopenharmony_ci * This function is used to free up any allocated resources for off-channel 482e5b75505Sopenharmony_ci * operations. 483e5b75505Sopenharmony_ci */ 484e5b75505Sopenharmony_civoid offchannel_deinit(struct wpa_supplicant *wpa_s) 485e5b75505Sopenharmony_ci{ 486e5b75505Sopenharmony_ci offchannel_clear_pending_action_tx(wpa_s); 487e5b75505Sopenharmony_ci eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL); 488e5b75505Sopenharmony_ci} 489