P2P: Allow P2P listen being offloaded to the driver/firmware
authorPeng Xu <pxu@qca.qualcomm.com>
Fri, 24 Jun 2016 18:36:18 +0000 (11:36 -0700)
committerJouni Malinen <j@w1.fi>
Sun, 3 Jul 2016 19:36:58 +0000 (22:36 +0300)
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>
15 files changed:
src/common/wpa_ctrl.h
src/drivers/driver.h
src/drivers/driver_common.c
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211_capa.c
src/drivers/driver_nl80211_event.c
src/p2p/p2p.c
src/p2p/p2p.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/driver_i.h
wpa_supplicant/events.c
wpa_supplicant/p2p_supplicant.c
wpa_supplicant/p2p_supplicant.h
wpa_supplicant/wpa_cli.c
wpa_supplicant/wpa_supplicant_i.h

index df26c7b..e10bcfe 100644 (file)
@@ -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 "
 
index a9dbdef..671a4b8 100644 (file)
@@ -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;
 };
 
 /**
index b32d35f..a1141ab 100644 (file)
@@ -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";
index 1afb99a..b2b27a7 100644 (file)
@@ -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,
index 1ebbdaa..1134886 100644 (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,
                          &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);
 }
 
index 8e3ff4c..762e3ac 100644 (file)
@@ -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,
index a209a56..47abe21 100644 (file)
@@ -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;
+}
index 186af36..5b5a0bf 100644 (file)
@@ -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 */
index e75323d..2d53174 100644 (file)
@@ -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) {
index 9f104f5..396a0dc 100644 (file)
@@ -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 */
index b7a3bc0..dffba32 100644 (file)
@@ -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;
index 8c5af5e..cb8df66 100644 (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)
                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;
+}
index 6a770d2..63910d1 100644 (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,
                         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 */
 
index 53036ae..ede66b7 100644 (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 {
        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,
          "<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 }
 };
 
index e45f662..dd6a60d 100644 (file)
@@ -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;