aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeng Xu <pxu@qca.qualcomm.com>2016-06-24 11:36:18 -0700
committerLinux Build Service Account <lnxbuild@localhost>2016-08-24 08:07:44 -0600
commit8a61dfcbafd5167a46b803802bad2b87a7d80592 (patch)
treebcf3ffddc3086489483417086ad6567fddba187e
parente8d73f1170d6640eb0b0c58de6f9011211134aeb (diff)
downloadandroid_external_wpa_supplicant_8-8a61dfcbafd5167a46b803802bad2b87a7d80592.tar.gz
android_external_wpa_supplicant_8-8a61dfcbafd5167a46b803802bad2b87a7d80592.tar.bz2
android_external_wpa_supplicant_8-8a61dfcbafd5167a46b803802bad2b87a7d80592.zip
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> Git-commit: a6f5b1937ad45ced659d87b1eb5a665c8d137d34 Git-repo : git://w1.fi/srv/git/hostap.git Change-Id: Ie59acdfd26c26548d5e171dcb0ea4bb8a8083e28 CRs-fixed: 1039034
-rw-r--r--src/common/wpa_ctrl.h3
-rw-r--r--src/drivers/driver.h54
-rw-r--r--src/drivers/driver_common.c1
-rw-r--r--src/drivers/driver_nl80211.c85
-rw-r--r--src/drivers/driver_nl80211_capa.c2
-rw-r--r--src/drivers/driver_nl80211_event.c27
-rw-r--r--src/p2p/p2p.c132
-rw-r--r--src/p2p/p2p.h5
-rw-r--r--wpa_supplicant/ctrl_iface.c21
-rw-r--r--wpa_supplicant/driver_i.h22
-rw-r--r--wpa_supplicant/events.c8
-rw-r--r--wpa_supplicant/p2p_supplicant.c91
-rw-r--r--wpa_supplicant/p2p_supplicant.h4
-rw-r--r--wpa_supplicant/wpa_cli.c20
-rw-r--r--wpa_supplicant/wpa_supplicant_i.h1
15 files changed, 431 insertions, 45 deletions
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 7eee472b..cb4fbc49 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -268,6 +268,9 @@ extern "C" {
#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 */
#define BSS_TM_RESP "BSS-TM-RESP "
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index b7e0d163..40d95bf6 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1271,6 +1271,8 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL
/** Driver supports full AP client state */
#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL
+/** Driver supports P2P Listen offload */
+#define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD 0x0000020000000000ULL
u64 flags;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -3537,6 +3539,32 @@ struct wpa_driver_ops {
* Returns 0 on success, -1 on failure
*/
int (*abort_scan)(void *priv);
+
+ /**
+ * 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);
};
@@ -4021,6 +4049,11 @@ enum wpa_event_type {
* on a DFS frequency by a driver that supports DFS Offload.
*/
EVENT_DFS_CAC_STARTED,
+
+ /**
+ * EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped
+ */
+ EVENT_P2P_LO_STOP,
};
@@ -4740,6 +4773,27 @@ union wpa_event_data {
u16 ch_width;
enum hostapd_hw_mode hw_mode;
} 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;
};
/**
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index b32d35f4..a1141ab9 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -80,6 +80,7 @@ const char * event_to_string(enum wpa_event_type event)
E2S(NEW_PEER_CANDIDATE);
E2S(ACS_CHANNEL_SELECTED);
E2S(DFS_CAC_STARTED);
+ E2S(P2P_LO_STOP);
}
return "UNKNOWN";
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 798e6947..f9837776 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -9050,8 +9050,89 @@ static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
return 0;
}
-#endif /* CONFIG_DRIVER_NL80211_QCA */
+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 */
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
@@ -9168,5 +9249,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.set_band = nl80211_set_band,
.get_pref_freq_list = nl80211_get_pref_freq_list,
.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 */
};
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 14a93a05..2b66a805 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -884,6 +884,8 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
&info))
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;
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index bd16edbd..244718e4 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -1869,8 +1869,30 @@ static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv,
external_scan);
}
-#endif /* CONFIG_DRIVER_NL80211_QCA */
+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 */
static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
u32 subcmd, u8 *data, size_t len)
@@ -1902,6 +1924,9 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE:
qca_nl80211_scan_done_event(drv, data, len);
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 */
default:
wpa_printf(MSG_DEBUG,
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index a209a56d..47abe214 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -2234,6 +2234,58 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p,
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)
{
@@ -2267,10 +2319,8 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
{
struct ieee802_11_elems elems;
struct wpabuf *buf;
- struct ieee80211_mgmt *resp;
struct p2p_message msg;
struct wpabuf *ies;
- u8 channel, op_class;
if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
ParseFailed) {
@@ -2414,49 +2464,12 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
return P2P_PREQ_NOT_PROCESSED;
}
- 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)) {
+ if (p2p_build_probe_resp_buf(p2p, buf, ies, addr, rx_freq)) {
wpabuf_free(ies);
wpabuf_free(buf);
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);
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
p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
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;
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);
if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED)
return res;
@@ -5490,3 +5509,34 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
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;
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 0feafd31..2c41dd96 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -1555,12 +1555,13 @@ enum p2p_probe_req_status {
* @ie: Information elements from the Probe Request frame body
* @ie_len: Length of ie buffer in octets
* @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
*/
enum p2p_probe_req_status
p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
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
@@ -2373,4 +2374,6 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
unsigned int *num);
+struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p,
+ unsigned int freq);
#endif /* P2P_H */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index ee7020aa..504023af 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -6229,6 +6229,21 @@ static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd)
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 */
@@ -8738,6 +8753,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) {
if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0)
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 */
#ifdef CONFIG_WIFI_DISPLAY
} else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 699fd4f7..fd56c0cd 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -917,4 +917,26 @@ static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s)
return wpa_s->driver->abort_scan(wpa_s->drv_priv);
}
+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 */
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index d0a0bacf..34ea74fe 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -4027,6 +4027,14 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
&data->acs_selected_channels);
#endif /* CONFIG_ACS */
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:
wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
break;
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 80d51ff3..527d1595 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -6643,6 +6643,12 @@ int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout)
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
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);
wpas_p2p_clear_pending_action_tx(wpa_s);
@@ -6716,7 +6722,7 @@ int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
return 0;
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:
wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
ssi_signal);
@@ -9219,3 +9225,86 @@ void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
wpa_s->ap_iface->bss[0]->p2p_group = NULL;
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;
+}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 6a770d2a..63910d1c 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -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,
struct wps_event_fail *fail);
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 */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 36a7a4eb..ea17bc8f 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -2885,6 +2885,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 {
cli_cmd_flag_none = 0x00,
cli_cmd_flag_sensitive = 0x01
@@ -3468,6 +3482,12 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
{ "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
cli_cmd_flag_none,
"<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 }
};
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index c4858917..b07255ef 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -898,6 +898,7 @@ struct wpa_supplicant {
unsigned int p2p_go_max_oper_chwidth;
unsigned int p2p_go_vht_center_freq2;
+ int p2p_lo_started;
#endif /* CONFIG_P2P */
struct wpa_ssid *bgscan_ssid;