From a6f5b1937ad45ced659d87b1eb5a665c8d137d34 Mon Sep 17 00:00:00 2001 From: Peng Xu Date: Fri, 24 Jun 2016 11:36:18 -0700 Subject: [PATCH] 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 To stop P2P listen offload, issue the command: p2p_lo_stop Signed-off-by: Jouni Malinen --- src/common/wpa_ctrl.h | 3 + src/drivers/driver.h | 54 +++++++++++++++ src/drivers/driver_common.c | 1 + src/drivers/driver_nl80211.c | 85 ++++++++++++++++++++++++ src/drivers/driver_nl80211_capa.c | 2 + src/drivers/driver_nl80211_event.c | 28 ++++++++ src/p2p/p2p.c | 132 +++++++++++++++++++++++++------------ src/p2p/p2p.h | 6 +- wpa_supplicant/ctrl_iface.c | 21 ++++++ wpa_supplicant/driver_i.h | 23 +++++++ wpa_supplicant/events.c | 8 +++ wpa_supplicant/p2p_supplicant.c | 91 ++++++++++++++++++++++++- wpa_supplicant/p2p_supplicant.h | 4 ++ wpa_supplicant/wpa_cli.c | 20 ++++++ wpa_supplicant/wpa_supplicant_i.h | 1 + 15 files changed, 436 insertions(+), 43 deletions(-) diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index df26c7b..e10bcfe 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -271,6 +271,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 a9dbdef..671a4b8 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1278,6 +1278,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) \ @@ -3573,6 +3575,32 @@ struct wpa_driver_ops { int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type, const u8 **ext_capab, const u8 **ext_capab_mask, 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. */ 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; 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 b32d35f..a1141ab 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 1afb99a..b2b27a7 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -9117,6 +9117,89 @@ static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq) 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 */ @@ -9357,6 +9440,8 @@ 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 */ .configure_data_frame_filters = nl80211_configure_data_frame_filters, .get_ext_capab = nl80211_get_ext_capab, diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 1ebbdaa..1134886 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -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, &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; os_free(info.flags); } diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 8e3ff4c..762e3ac 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -1876,6 +1876,31 @@ static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv, 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 */ @@ -1909,6 +1934,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 a209a56..47abe21 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 186af36..5b5a0bf 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 @@ -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, 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 e75323d..2d53174 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -6283,6 +6283,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 */ @@ -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) { 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 9f104f5..396a0dc 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -946,4 +946,27 @@ static inline int wpa_drv_get_ext_capa(struct wpa_supplicant *wpa_s, &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 */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index b7a3bc0..dffba32 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -4067,6 +4067,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 8c5af5e..cb8df66 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -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) 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); @@ -6705,7 +6711,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); @@ -9206,3 +9212,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 6a770d2..63910d1 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 53036ae..ede66b7 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -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 { cli_cmd_flag_none = 0x00, 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, cli_cmd_flag_none, " = retrieve preferred freq list for the specified interface type" }, + { "p2p_lo_start", wpa_cli_cmd_p2p_lo_start, NULL, + cli_cmd_flag_none, + " = 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 e45f662..dd6a60d 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -893,6 +893,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; -- 2.1.4