P2P: Retry provision discovery requests in IDLE state

Since the peer may not be in Listen state when the provision discovery
request is sent, try to send the request again number of times when in
IDLE state. This was already done when p2p_find is in progress, but this
commit adds retries to the case where no other P2P operations are in
progress.

Signed-off-by: Jayant Sane <jayant.sane@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Jayant Sane 2011-06-12 14:20:39 -07:00 committed by Jouni Malinen
parent 6b98a33c86
commit 6b56cc2d97
3 changed files with 155 additions and 1 deletions

View file

@ -2250,19 +2250,81 @@ static void p2p_sd_cb(struct p2p_data *p2p, int success)
p2p_set_timeout(p2p, 0, 200000); 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) static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
{ {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Provision Discovery Request TX callback: success=%d", "P2P: Provision Discovery Request TX callback: success=%d",
success); 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) { if (!success) {
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (p2p->state != P2P_IDLE) if (p2p->state != P2P_IDLE)
p2p_continue_find(p2p); p2p_continue_find(p2p);
else if (p2p->user_initiated_pd) {
p2p->pending_action_state = P2P_PENDING_PD;
p2p_set_timeout(p2p, 0, 300000);
}
return; 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 */ /* Wait for response from the peer */
if (p2p->state == P2P_SEARCH) if (p2p->state == P2P_SEARCH)
p2p_set_state(p2p, P2P_PD_DURING_FIND); 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) static void p2p_timeout_invite(struct p2p_data *p2p)
{ {
p2p->cfg->send_action_done(p2p->cfg->cb_ctx); 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) { switch (p2p->state) {
case P2P_IDLE: 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; break;
case P2P_SEARCH: 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); p2p_search(p2p);
break; break;
case P2P_CONNECT: case P2P_CONNECT:
@ -2714,6 +2806,10 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
case P2P_GO_NEG: case P2P_GO_NEG:
break; break;
case P2P_LISTEN_ONLY: 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) { if (p2p->ext_listen_only) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Extended Listen Timing - Listen State " "P2P: Extended Listen Timing - Listen State "

View file

@ -393,6 +393,23 @@ struct p2p_data {
* wps_vendor_ext - WPS Vendor Extensions to add * wps_vendor_ext - WPS Vendor Extensions to add
*/ */
struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; 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); const u8 *data, size_t len);
int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
int join); int join);
void p2p_reset_pending_pd(struct p2p_data *p2p);
/* p2p_invitation.c */ /* p2p_invitation.c */
void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,

View file

@ -21,6 +21,13 @@
#include "p2p.h" #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, static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
u16 config_methods) u16 config_methods)
{ {
@ -215,6 +222,11 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
return; 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) { if (dev->dialog_token != msg.dialog_token) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Ignore Provisioning Discovery Response with " "P2P: Ignore Provisioning Discovery Response with "
@ -224,6 +236,14 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
return; 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) { if (msg.wps_config_methods != dev->req_config_methods) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected " wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected "
"our Provisioning Discovery Request"); "our Provisioning Discovery Request");
@ -301,6 +321,8 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
return -1; return -1;
} }
os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
wpabuf_free(req); wpabuf_free(req);
return 0; return 0;
} }
@ -343,5 +365,23 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
return 0; 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); 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;
}