diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 653609e33..d16d555d9 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -2250,19 +2250,81 @@ static void p2p_sd_cb(struct p2p_data *p2p, int success) p2p_set_timeout(p2p, 0, 200000); } + +/** + * p2p_retry_pd - Retry any pending provision disc requests in IDLE state + * @p2p: P2P module context from p2p_init() + */ +void p2p_retry_pd(struct p2p_data *p2p) +{ + struct p2p_device *dev; + + if (p2p->state != P2P_IDLE) + return; + + /* + * Retry the prov disc req attempt only for the peer that the user had + * requested for and provided a join has not been initiated on it + * in the meantime. + */ + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(p2p->pending_pd_devaddr, + dev->info.p2p_device_addr, ETH_ALEN) != 0) + continue; + if (!dev->req_config_methods) + continue; + if (dev->flags & P2P_DEV_PD_FOR_JOIN) + continue; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send " + "pending Provisioning Discovery Request to " + MACSTR " (config methods 0x%x)", + MAC2STR(dev->info.p2p_device_addr), + dev->req_config_methods); + p2p_send_prov_disc_req(p2p, dev, 0); + return; + } +} + + static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery Request TX callback: success=%d", success); - p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + /* + * Postpone resetting the pending action state till after we actually + * time out. This allows us to take some action like notifying any + * interested parties about no response to the request. + * + * When the timer (below) goes off we check in IDLE, SEARCH, or + * LISTEN_ONLY state, which are the only allowed states to issue a PD + * requests in, if this was still pending and then raise notification. + */ if (!success) { + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p->state != P2P_IDLE) p2p_continue_find(p2p); + else if (p2p->user_initiated_pd) { + p2p->pending_action_state = P2P_PENDING_PD; + p2p_set_timeout(p2p, 0, 300000); + } return; } + /* + * This postponing, of resetting pending_action_state, needs to be + * done only for user initiated PD requests and not internal ones. + */ + if (p2p->user_initiated_pd) + p2p->pending_action_state = P2P_PENDING_PD; + else + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + /* Wait for response from the peer */ if (p2p->state == P2P_SEARCH) p2p_set_state(p2p, P2P_PD_DURING_FIND); @@ -2653,6 +2715,30 @@ static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p) } +static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) +{ + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + /* + * For user initiated PD requests that we have not gotten any responses + * for while in IDLE state, we retry them a couple of times before + * giving up. + */ + if (!p2p->user_initiated_pd) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: User initiated Provision Discovery Request timeout"); + + if (p2p->pd_retries) { + p2p->pd_retries--; + p2p_retry_pd(p2p); + } else { + p2p_reset_pending_pd(p2p); + } +} + + static void p2p_timeout_invite(struct p2p_data *p2p) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); @@ -2701,8 +2787,14 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) switch (p2p->state) { case P2P_IDLE: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); break; case P2P_SEARCH: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); p2p_search(p2p); break; case P2P_CONNECT: @@ -2714,6 +2806,10 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) case P2P_GO_NEG: break; case P2P_LISTEN_ONLY: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); + if (p2p->ext_listen_only) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen Timing - Listen State " diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 68b119406..ef90f0ab2 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -393,6 +393,23 @@ struct p2p_data { * wps_vendor_ext - WPS Vendor Extensions to add */ struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + + /* + * user_initiated_pd - Whether a PD request is user initiated or not. + */ + u8 user_initiated_pd; + + /* + * Keep track of which peer a given PD request was sent to. + * Used to raise a timeout alert in case there is no response. + */ + u8 pending_pd_devaddr[ETH_ALEN]; + + /* + * Retry counter for provision discovery requests when issued + * in IDLE state. + */ + int pd_retries; }; /** @@ -586,6 +603,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join); +void p2p_reset_pending_pd(struct p2p_data *p2p); /* p2p_invitation.c */ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index e0642168f..443e37d25 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -21,6 +21,13 @@ #include "p2p.h" +/* + * Number of retries to attempt for provision discovery requests during IDLE + * state in case the peer is not listening. + */ +#define MAX_PROV_DISC_REQ_RETRIES 10 + + static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, u16 config_methods) { @@ -215,6 +222,11 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, return; } + if (p2p->pending_action_state == P2P_PENDING_PD) { + os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + } + if (dev->dialog_token != msg.dialog_token) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore Provisioning Discovery Response with " @@ -224,6 +236,14 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, return; } + /* + * If the response is from the peer to whom a user initiated request + * was sent earlier, we reset that state info here. + */ + if (p2p->user_initiated_pd && + os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) + p2p_reset_pending_pd(p2p); + if (msg.wps_config_methods != dev->req_config_methods) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected " "our Provisioning Discovery Request"); @@ -301,6 +321,8 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, return -1; } + os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN); + wpabuf_free(req); return 0; } @@ -343,5 +365,23 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, return 0; } + /* + * We use the join param as a cue to differentiate between user + * initiated PD request and one issued during finds (internal). + */ + p2p->user_initiated_pd = !join; + + /* Also set some retries to attempt in case of IDLE state */ + if (p2p->user_initiated_pd && p2p->state == P2P_IDLE) + p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; + return p2p_send_prov_disc_req(p2p, dev, join); } + + +void p2p_reset_pending_pd(struct p2p_data *p2p) +{ + p2p->user_initiated_pd = 0; + os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); + p2p->pd_retries = 0; +}