P2P: Allow P2P listen being offloaded to the driver/firmware

This allows P2P Listen to be offloaded to device to enhance power
saving.

To start P2P listen offload, from wpa_cli interface, issue the command:
	p2p_lo_start <freq> <period> <interval> <count>

To stop P2P listen offload, issue the command:
	p2p_lo_stop

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Peng Xu 2016-06-24 11:36:18 -07:00 committed by Jouni Malinen
parent 35d6655755
commit a6f5b1937a
15 changed files with 436 additions and 43 deletions

View file

@ -271,6 +271,9 @@ extern "C" {
#define AP_CSA_FINISHED "AP-CSA-FINISHED " #define AP_CSA_FINISHED "AP-CSA-FINISHED "
#define P2P_EVENT_LISTEN_OFFLOAD_STOP "P2P-LISTEN-OFFLOAD-STOPPED "
#define P2P_LISTEN_OFFLOAD_STOP_REASON "P2P-LISTEN-OFFLOAD-STOP-REASON "
/* BSS Transition Management Response frame received */ /* BSS Transition Management Response frame received */
#define BSS_TM_RESP "BSS-TM-RESP " #define BSS_TM_RESP "BSS-TM-RESP "

View file

@ -1278,6 +1278,8 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL #define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL
/** Driver supports full AP client state */ /** Driver supports full AP client state */
#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL #define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL
/** Driver supports P2P Listen offload */
#define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD 0x0000020000000000ULL
u64 flags; u64 flags;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@ -3573,6 +3575,32 @@ struct wpa_driver_ops {
int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type, int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type,
const u8 **ext_capab, const u8 **ext_capab_mask, const u8 **ext_capab, const u8 **ext_capab_mask,
unsigned int *ext_capab_len); unsigned int *ext_capab_len);
/**
* p2p_lo_start - Start offloading P2P listen to device
* @priv: Private driver interface data
* @freq: Listening frequency (MHz) for P2P listen
* @period: Length of the listen operation in milliseconds
* @interval: Interval for running the listen operation in milliseconds
* @count: Number of times to run the listen operation
* @device_types: Device primary and secondary types
* @dev_types_len: Number of bytes for device_types
* @ies: P2P IE and WSC IE for Probe Response frames
* @ies_len: Length of ies in bytes
* Returns: 0 on success or -1 on failure
*/
int (*p2p_lo_start)(void *priv, unsigned int freq,
unsigned int period, unsigned int interval,
unsigned int count,
const u8 *device_types, size_t dev_types_len,
const u8 *ies, size_t ies_len);
/**
* p2p_lo_stop - Stop P2P listen offload
* @priv: Private driver interface data
* Returns: 0 on success or -1 on failure
*/
int (*p2p_lo_stop)(void *priv);
}; };
@ -4057,6 +4085,11 @@ enum wpa_event_type {
* on a DFS frequency by a driver that supports DFS Offload. * on a DFS frequency by a driver that supports DFS Offload.
*/ */
EVENT_DFS_CAC_STARTED, EVENT_DFS_CAC_STARTED,
/**
* EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped
*/
EVENT_P2P_LO_STOP,
}; };
@ -4782,6 +4815,27 @@ union wpa_event_data {
u16 ch_width; u16 ch_width;
enum hostapd_hw_mode hw_mode; enum hostapd_hw_mode hw_mode;
} acs_selected_channels; } acs_selected_channels;
/**
* struct p2p_lo_stop - Reason code for P2P Listen offload stop event
* @reason_code: Reason for stopping offload
* P2P_LO_STOPPED_REASON_COMPLETE: Listen offload finished as
* scheduled.
* P2P_LO_STOPPED_REASON_RECV_STOP_CMD: Host requested offload to
* be stopped.
* P2P_LO_STOPPED_REASON_INVALID_PARAM: Invalid listen offload
* parameters.
* P2P_LO_STOPPED_REASON_NOT_SUPPORTED: Listen offload not
* supported by device.
*/
struct p2p_lo_stop {
enum {
P2P_LO_STOPPED_REASON_COMPLETE = 0,
P2P_LO_STOPPED_REASON_RECV_STOP_CMD,
P2P_LO_STOPPED_REASON_INVALID_PARAM,
P2P_LO_STOPPED_REASON_NOT_SUPPORTED,
} reason_code;
} p2p_lo_stop;
}; };
/** /**

View file

@ -80,6 +80,7 @@ const char * event_to_string(enum wpa_event_type event)
E2S(NEW_PEER_CANDIDATE); E2S(NEW_PEER_CANDIDATE);
E2S(ACS_CHANNEL_SELECTED); E2S(ACS_CHANNEL_SELECTED);
E2S(DFS_CAC_STARTED); E2S(DFS_CAC_STARTED);
E2S(P2P_LO_STOP);
} }
return "UNKNOWN"; return "UNKNOWN";

View file

@ -9117,6 +9117,89 @@ static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
return 0; return 0;
} }
static int nl80211_p2p_lo_start(void *priv, unsigned int freq,
unsigned int period, unsigned int interval,
unsigned int count, const u8 *device_types,
size_t dev_types_len,
const u8 *ies, size_t ies_len)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *container;
int ret;
wpa_printf(MSG_DEBUG,
"nl80211: Start P2P Listen offload: freq=%u, period=%u, interval=%u, count=%u",
freq, period, interval, count);
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
return -1;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START))
goto fail;
container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
if (!container)
goto fail;
if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL,
freq) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD,
period) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL,
interval) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT,
count) ||
nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES,
dev_types_len, device_types) ||
nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE,
ies_len, ies))
goto fail;
nla_nest_end(msg, container);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to send P2P Listen offload vendor command");
goto fail;
}
return 0;
fail:
nlmsg_free(msg);
return -1;
}
static int nl80211_p2p_lo_stop(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
wpa_printf(MSG_DEBUG, "nl80211: Stop P2P Listen offload");
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
return -1;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP)) {
nlmsg_free(msg);
return -1;
}
return send_and_recv_msgs(drv, msg, NULL, NULL);
}
#endif /* CONFIG_DRIVER_NL80211_QCA */ #endif /* CONFIG_DRIVER_NL80211_QCA */
@ -9357,6 +9440,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.set_band = nl80211_set_band, .set_band = nl80211_set_band,
.get_pref_freq_list = nl80211_get_pref_freq_list, .get_pref_freq_list = nl80211_get_pref_freq_list,
.set_prob_oper_freq = nl80211_set_prob_oper_freq, .set_prob_oper_freq = nl80211_set_prob_oper_freq,
.p2p_lo_start = nl80211_p2p_lo_start,
.p2p_lo_stop = nl80211_p2p_lo_stop,
#endif /* CONFIG_DRIVER_NL80211_QCA */ #endif /* CONFIG_DRIVER_NL80211_QCA */
.configure_data_frame_filters = nl80211_configure_data_frame_filters, .configure_data_frame_filters = nl80211_configure_data_frame_filters,
.get_ext_capab = nl80211_get_ext_capab, .get_ext_capab = nl80211_get_ext_capab,

View file

@ -972,6 +972,8 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS, if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
&info)) &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS; drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD;
os_free(info.flags); os_free(info.flags);
} }

View file

@ -1876,6 +1876,31 @@ static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv,
external_scan); external_scan);
} }
static void qca_nl80211_p2p_lo_stop_event(struct wpa_driver_nl80211_data *drv,
u8 *data, size_t len)
{
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1];
union wpa_event_data event;
wpa_printf(MSG_DEBUG,
"nl80211: P2P listen offload stop vendor event received");
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX,
(struct nlattr *) data, len, NULL) ||
!tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON])
return;
os_memset(&event, 0, sizeof(event));
event.p2p_lo_stop.reason_code =
nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON]);
wpa_printf(MSG_DEBUG,
"nl80211: P2P Listen offload stop reason: %d",
event.p2p_lo_stop.reason_code);
wpa_supplicant_event(drv->ctx, EVENT_P2P_LO_STOP, &event);
}
#endif /* CONFIG_DRIVER_NL80211_QCA */ #endif /* CONFIG_DRIVER_NL80211_QCA */
@ -1909,6 +1934,9 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE: case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE:
qca_nl80211_scan_done_event(drv, data, len); qca_nl80211_scan_done_event(drv, data, len);
break; break;
case QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP:
qca_nl80211_p2p_lo_stop_event(drv, data, len);
break;
#endif /* CONFIG_DRIVER_NL80211_QCA */ #endif /* CONFIG_DRIVER_NL80211_QCA */
default: default:
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,

View file

@ -2234,6 +2234,58 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p,
return buf; return buf;
} }
static int p2p_build_probe_resp_buf(struct p2p_data *p2p, struct wpabuf *buf,
struct wpabuf *ies,
const u8 *addr, int rx_freq)
{
struct ieee80211_mgmt *resp;
u8 channel, op_class;
resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
u.probe_resp.variable));
resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_PROBE_RESP << 4));
os_memcpy(resp->da, addr, ETH_ALEN);
os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN);
os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN);
resp->u.probe_resp.beacon_int = host_to_le16(100);
/* hardware or low-level driver will setup seq_ctrl and timestamp */
resp->u.probe_resp.capab_info =
host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE |
WLAN_CAPABILITY_PRIVACY |
WLAN_CAPABILITY_SHORT_SLOT_TIME);
wpabuf_put_u8(buf, WLAN_EID_SSID);
wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN);
wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES);
wpabuf_put_u8(buf, 8);
wpabuf_put_u8(buf, (60 / 5) | 0x80);
wpabuf_put_u8(buf, 90 / 5);
wpabuf_put_u8(buf, (120 / 5) | 0x80);
wpabuf_put_u8(buf, 180 / 5);
wpabuf_put_u8(buf, (240 / 5) | 0x80);
wpabuf_put_u8(buf, 360 / 5);
wpabuf_put_u8(buf, 480 / 5);
wpabuf_put_u8(buf, 540 / 5);
if (!rx_freq) {
channel = p2p->cfg->channel;
} else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) {
p2p_err(p2p, "Failed to convert freq to channel");
return -1;
}
wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
wpabuf_put_u8(buf, 1);
wpabuf_put_u8(buf, channel);
wpabuf_put_buf(buf, ies);
return 0;
}
static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash)
{ {
@ -2267,10 +2319,8 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
{ {
struct ieee802_11_elems elems; struct ieee802_11_elems elems;
struct wpabuf *buf; struct wpabuf *buf;
struct ieee80211_mgmt *resp;
struct p2p_message msg; struct p2p_message msg;
struct wpabuf *ies; struct wpabuf *ies;
u8 channel, op_class;
if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
ParseFailed) { ParseFailed) {
@ -2414,49 +2464,12 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
return P2P_PREQ_NOT_PROCESSED; return P2P_PREQ_NOT_PROCESSED;
} }
resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, if (p2p_build_probe_resp_buf(p2p, buf, ies, addr, rx_freq)) {
u.probe_resp.variable));
resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_PROBE_RESP << 4));
os_memcpy(resp->da, addr, ETH_ALEN);
os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN);
os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN);
resp->u.probe_resp.beacon_int = host_to_le16(100);
/* hardware or low-level driver will setup seq_ctrl and timestamp */
resp->u.probe_resp.capab_info =
host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE |
WLAN_CAPABILITY_PRIVACY |
WLAN_CAPABILITY_SHORT_SLOT_TIME);
wpabuf_put_u8(buf, WLAN_EID_SSID);
wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN);
wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES);
wpabuf_put_u8(buf, 8);
wpabuf_put_u8(buf, (60 / 5) | 0x80);
wpabuf_put_u8(buf, 90 / 5);
wpabuf_put_u8(buf, (120 / 5) | 0x80);
wpabuf_put_u8(buf, 180 / 5);
wpabuf_put_u8(buf, (240 / 5) | 0x80);
wpabuf_put_u8(buf, 360 / 5);
wpabuf_put_u8(buf, 480 / 5);
wpabuf_put_u8(buf, 540 / 5);
if (!rx_freq) {
channel = p2p->cfg->channel;
} else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) {
wpabuf_free(ies); wpabuf_free(ies);
wpabuf_free(buf); wpabuf_free(buf);
return P2P_PREQ_NOT_PROCESSED; return P2P_PREQ_NOT_PROCESSED;
} }
wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
wpabuf_put_u8(buf, 1);
wpabuf_put_u8(buf, channel);
wpabuf_put_buf(buf, ies);
wpabuf_free(ies); wpabuf_free(ies);
p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq); p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq);
@ -2470,12 +2483,18 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
enum p2p_probe_req_status enum p2p_probe_req_status
p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
const u8 *bssid, const u8 *ie, size_t ie_len, const u8 *bssid, const u8 *ie, size_t ie_len,
unsigned int rx_freq) unsigned int rx_freq, int p2p_lo_started)
{ {
enum p2p_probe_req_status res; enum p2p_probe_req_status res;
p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
if (p2p_lo_started) {
p2p_dbg(p2p,
"Probe Response is offloaded, do not reply Probe Request");
return P2P_PREQ_PROCESSED;
}
res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq); res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq);
if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED) if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED)
return res; return res;
@ -5490,3 +5509,34 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
i, p2p->pref_freq_list[i]); i, p2p->pref_freq_list[i]);
} }
} }
struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p,
unsigned int freq)
{
struct wpabuf *ies, *buf;
u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
int ret;
ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
if (!ies) {
wpa_printf(MSG_ERROR,
"CTRL: Failed to build Probe Response IEs");
return NULL;
}
buf = wpabuf_alloc(200 + wpabuf_len(ies));
if (!buf) {
wpabuf_free(ies);
return NULL;
}
ret = p2p_build_probe_resp_buf(p2p, buf, ies, addr, freq);
wpabuf_free(ies);
if (ret) {
wpabuf_free(buf);
return NULL;
}
return buf;
}

View file

@ -1555,12 +1555,13 @@ enum p2p_probe_req_status {
* @ie: Information elements from the Probe Request frame body * @ie: Information elements from the Probe Request frame body
* @ie_len: Length of ie buffer in octets * @ie_len: Length of ie buffer in octets
* @rx_freq: Probe Request frame RX frequency * @rx_freq: Probe Request frame RX frequency
* @p2p_lo_started: Whether P2P Listen Offload is started
* Returns: value indicating the type and status of the probe request * Returns: value indicating the type and status of the probe request
*/ */
enum p2p_probe_req_status enum p2p_probe_req_status
p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
const u8 *bssid, const u8 *ie, size_t ie_len, const u8 *bssid, const u8 *ie, size_t ie_len,
unsigned int rx_freq); unsigned int rx_freq, int p2p_lo_started);
/** /**
* p2p_rx_action - Report received Action frame * p2p_rx_action - Report received Action frame
@ -2383,4 +2384,7 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
unsigned int *num); unsigned int *num);
struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p,
unsigned int freq);
#endif /* P2P_H */ #endif /* P2P_H */

View file

@ -6283,6 +6283,21 @@ static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd)
return 0; return 0;
} }
static int p2p_ctrl_iface_p2p_lo_start(struct wpa_supplicant *wpa_s, char *cmd)
{
int freq = 0, period = 0, interval = 0, count = 0;
if (sscanf(cmd, "%d %d %d %d", &freq, &period, &interval, &count) != 4)
{
wpa_printf(MSG_DEBUG,
"CTRL: Invalid P2P LO Start parameter: '%s'", cmd);
return -1;
}
return wpas_p2p_lo_start(wpa_s, freq, period, interval, count);
}
#endif /* CONFIG_P2P */ #endif /* CONFIG_P2P */
@ -8968,6 +8983,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) { } else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) {
if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0) if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0)
reply_len = -1; reply_len = -1;
} else if (os_strncmp(buf, "P2P_LO_START ", 13) == 0) {
if (p2p_ctrl_iface_p2p_lo_start(wpa_s, buf + 13))
reply_len = -1;
} else if (os_strcmp(buf, "P2P_LO_STOP") == 0) {
if (wpas_p2p_lo_stop(wpa_s))
reply_len = -1;
#endif /* CONFIG_P2P */ #endif /* CONFIG_P2P */
#ifdef CONFIG_WIFI_DISPLAY #ifdef CONFIG_WIFI_DISPLAY
} else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) { } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {

View file

@ -946,4 +946,27 @@ static inline int wpa_drv_get_ext_capa(struct wpa_supplicant *wpa_s,
&wpa_s->extended_capa_len); &wpa_s->extended_capa_len);
} }
static inline int wpa_drv_p2p_lo_start(struct wpa_supplicant *wpa_s,
unsigned int channel,
unsigned int period,
unsigned int interval,
unsigned int count,
const u8 *device_types,
size_t dev_types_len,
const u8 *ies, size_t ies_len)
{
if (!wpa_s->driver->p2p_lo_start)
return -1;
return wpa_s->driver->p2p_lo_start(wpa_s->drv_priv, channel, period,
interval, count, device_types,
dev_types_len, ies, ies_len);
}
static inline int wpa_drv_p2p_lo_stop(struct wpa_supplicant *wpa_s)
{
if (!wpa_s->driver->p2p_lo_stop)
return -1;
return wpa_s->driver->p2p_lo_stop(wpa_s->drv_priv);
}
#endif /* DRIVER_I_H */ #endif /* DRIVER_I_H */

View file

@ -4067,6 +4067,14 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
&data->acs_selected_channels); &data->acs_selected_channels);
#endif /* CONFIG_ACS */ #endif /* CONFIG_ACS */
break; break;
case EVENT_P2P_LO_STOP:
#ifdef CONFIG_P2P
wpa_s->p2p_lo_started = 0;
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_LISTEN_OFFLOAD_STOP
P2P_LISTEN_OFFLOAD_STOP_REASON "reason=%d",
data->p2p_lo_stop.reason_code);
#endif /* CONFIG_P2P */
break;
default: default:
wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
break; break;

View file

@ -6632,6 +6632,12 @@ int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout)
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1; return -1;
if (wpa_s->p2p_lo_started) {
wpa_printf(MSG_DEBUG,
"P2P: Cannot start P2P listen, it is offloaded");
return -1;
}
wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_sched_scan(wpa_s);
wpas_p2p_clear_pending_action_tx(wpa_s); wpas_p2p_clear_pending_action_tx(wpa_s);
@ -6705,7 +6711,7 @@ int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
return 0; return 0;
switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid, switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
ie, ie_len, rx_freq)) { ie, ie_len, rx_freq, wpa_s->p2p_lo_started)) {
case P2P_PREQ_NOT_P2P: case P2P_PREQ_NOT_P2P:
wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len, wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
ssi_signal); ssi_signal);
@ -9206,3 +9212,86 @@ void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
wpa_s->ap_iface->bss[0]->p2p_group = NULL; wpa_s->ap_iface->bss[0]->p2p_group = NULL;
wpas_p2p_group_deinit(wpa_s); wpas_p2p_group_deinit(wpa_s);
} }
int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq,
unsigned int period, unsigned int interval,
unsigned int count)
{
struct p2p_data *p2p = wpa_s->global->p2p;
u8 *device_types;
size_t dev_types_len;
struct wpabuf *buf;
int ret;
if (wpa_s->p2p_lo_started) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P Listen offload is already started");
return 0;
}
if (wpa_s->global->p2p == NULL ||
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD)) {
wpa_printf(MSG_DEBUG, "P2P: Listen offload not supported");
return -1;
}
if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
wpa_printf(MSG_ERROR, "P2P: Input channel not supported: %u",
freq);
return -1;
}
/* Get device type */
dev_types_len = (wpa_s->conf->num_sec_device_types + 1) *
WPS_DEV_TYPE_LEN;
device_types = os_malloc(dev_types_len);
if (!device_types)
return -1;
os_memcpy(device_types, wpa_s->conf->device_type, WPS_DEV_TYPE_LEN);
os_memcpy(&device_types[WPS_DEV_TYPE_LEN], wpa_s->conf->sec_device_type,
wpa_s->conf->num_sec_device_types * WPS_DEV_TYPE_LEN);
/* Get Probe Response IE(s) */
buf = p2p_build_probe_resp_template(p2p, freq);
if (!buf) {
os_free(device_types);
return -1;
}
ret = wpa_drv_p2p_lo_start(wpa_s, freq, period, interval, count,
device_types, dev_types_len,
wpabuf_mhead_u8(buf), wpabuf_len(buf));
if (ret < 0)
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Failed to start P2P listen offload");
os_free(device_types);
wpabuf_free(buf);
if (ret == 0) {
wpa_s->p2p_lo_started = 1;
/* Stop current P2P listen if any */
wpas_stop_listen(wpa_s);
}
return ret;
}
int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s)
{
int ret;
if (!wpa_s->p2p_lo_started)
return 0;
ret = wpa_drv_p2p_lo_stop(wpa_s);
if (ret < 0)
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Failed to stop P2P listen offload");
wpa_s->p2p_lo_started = 0;
return ret;
}

View file

@ -207,6 +207,10 @@ int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s);
void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
struct wps_event_fail *fail); struct wps_event_fail *fail);
int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname); int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq,
unsigned int period, unsigned int interval,
unsigned int count);
int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s);
#else /* CONFIG_P2P */ #else /* CONFIG_P2P */

View file

@ -2892,6 +2892,20 @@ static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc,
} }
static int wpa_cli_cmd_p2p_lo_start(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_LO_START", 4, argc, argv);
}
static int wpa_cli_cmd_p2p_lo_stop(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_LO_STOP", 0, argc, argv);
}
enum wpa_cli_cmd_flags { enum wpa_cli_cmd_flags {
cli_cmd_flag_none = 0x00, cli_cmd_flag_none = 0x00,
cli_cmd_flag_sensitive = 0x01 cli_cmd_flag_sensitive = 0x01
@ -3477,6 +3491,12 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
{ "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL, { "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
cli_cmd_flag_none, cli_cmd_flag_none,
"<interface type> = retrieve preferred freq list for the specified interface type" }, "<interface type> = retrieve preferred freq list for the specified interface type" },
{ "p2p_lo_start", wpa_cli_cmd_p2p_lo_start, NULL,
cli_cmd_flag_none,
"<freq> <period> <interval> <count> = start P2P listen offload" },
{ "p2p_lo_stop", wpa_cli_cmd_p2p_lo_stop, NULL,
cli_cmd_flag_none,
"= stop P2P listen offload" },
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL } { NULL, NULL, NULL, cli_cmd_flag_none, NULL }
}; };

View file

@ -893,6 +893,7 @@ struct wpa_supplicant {
unsigned int p2p_go_max_oper_chwidth; unsigned int p2p_go_max_oper_chwidth;
unsigned int p2p_go_vht_center_freq2; unsigned int p2p_go_vht_center_freq2;
int p2p_lo_started;
#endif /* CONFIG_P2P */ #endif /* CONFIG_P2P */
struct wpa_ssid *bgscan_ssid; struct wpa_ssid *bgscan_ssid;