P2P: Allow Device ID to be specified for p2p_find command
[mech_eap.git] / src / p2p / p2p.c
index c0c59a6..e61e133 100644 (file)
@@ -106,12 +106,42 @@ static const char * p2p_state_txt(int state)
                return "INVITE";
        case P2P_INVITE_LISTEN:
                return "INVITE_LISTEN";
+       case P2P_SEARCH_WHEN_READY:
+               return "SEARCH_WHEN_READY";
        default:
                return "?";
        }
 }
 
 
+u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
+{
+       struct p2p_device *dev = NULL;
+
+       if (!addr || !p2p)
+               return 0;
+
+       dev = p2p_get_device(p2p, addr);
+       if (dev)
+               return dev->wps_prov_info;
+       else
+               return 0;
+}
+
+
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *iface_addr)
+{
+       struct p2p_device *dev = NULL;
+
+       if (!iface_addr || !p2p)
+               return;
+
+       dev = p2p_get_device_interface(p2p, iface_addr);
+       if (dev)
+               dev->wps_prov_info = 0;
+}
+
+
 void p2p_set_state(struct p2p_data *p2p, int new_state)
 {
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s",
@@ -144,6 +174,8 @@ void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
        struct p2p_go_neg_results res;
        p2p_clear_timeout(p2p);
        p2p_set_state(p2p, P2P_IDLE);
+       if (p2p->go_neg_peer)
+               p2p->go_neg_peer->wps_method = WPS_NOT_READY;
        p2p->go_neg_peer = NULL;
 
        os_memset(&res, 0, sizeof(res));
@@ -219,6 +251,12 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
        p2p->pending_listen_usec = (timeout % 1000) * 1000;
 
        if (p2p->p2p_scan_running) {
+               if (p2p->start_after_scan == P2P_AFTER_SCAN_NOTHING) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: p2p_scan running - connect is already "
+                               "pending - skip listen");
+                       return 0;
+               }
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                        "P2P: p2p_scan running - delay start of listen state");
                p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN;
@@ -363,7 +401,7 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
         * group, the information will be restored in the loop following this.
         */
        dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
-               if (os_memcpy(dev->member_in_go_iface, go_interface_addr,
+               if (os_memcmp(dev->member_in_go_iface, go_interface_addr,
                              ETH_ALEN) == 0) {
                        os_memset(dev->member_in_go_iface, 0, ETH_ALEN);
                        os_memset(dev->member_in_go_dev, 0, ETH_ALEN);
@@ -372,6 +410,9 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
 
        for (c = 0; c < info.num_clients; c++) {
                struct p2p_client_info *cli = &info.client[c];
+               if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr,
+                             ETH_ALEN) == 0)
+                       continue; /* ignore our own entry */
                dev = p2p_get_device(p2p, cli->p2p_device_addr);
                if (dev) {
                        /*
@@ -625,8 +666,13 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
 {
        int i;
 
-       if (p2p->go_neg_peer == dev)
+       if (p2p->go_neg_peer == dev) {
+               /*
+                * If GO Negotiation is in progress, report that it has failed.
+                */
+               p2p_go_neg_failed(p2p, dev, -1);
                p2p->go_neg_peer = NULL;
+       }
        if (p2p->invite_peer == dev)
                p2p->invite_peer = NULL;
        if (p2p->sd_peer == dev)
@@ -749,8 +795,8 @@ static void p2p_search(struct p2p_data *p2p)
        }
 
        if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
-                              p2p->num_req_dev_types, p2p->req_dev_types) < 0)
-       {
+                              p2p->num_req_dev_types, p2p->req_dev_types,
+                              p2p->find_dev_id) < 0) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                        "P2P: Scan request failed");
                p2p_continue_find(p2p);
@@ -778,19 +824,18 @@ static int p2p_run_after_scan(struct p2p_data *p2p)
        enum p2p_after_scan op;
 
        if (p2p->after_scan_tx) {
-               int ret;
                /* TODO: schedule p2p_run_after_scan to be called from TX
                 * status callback(?) */
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send pending "
                        "Action frame at p2p_scan completion");
-               ret = p2p->cfg->send_action(p2p->cfg->cb_ctx,
-                                           p2p->after_scan_tx->freq,
-                                           p2p->after_scan_tx->dst,
-                                           p2p->after_scan_tx->src,
-                                           p2p->after_scan_tx->bssid,
-                                           (u8 *) (p2p->after_scan_tx + 1),
-                                           p2p->after_scan_tx->len,
-                                           p2p->after_scan_tx->wait_time);
+               p2p->cfg->send_action(p2p->cfg->cb_ctx,
+                                     p2p->after_scan_tx->freq,
+                                     p2p->after_scan_tx->dst,
+                                     p2p->after_scan_tx->src,
+                                     p2p->after_scan_tx->bssid,
+                                     (u8 *) (p2p->after_scan_tx + 1),
+                                     p2p->after_scan_tx->len,
+                                     p2p->after_scan_tx->wait_time);
                os_free(p2p->after_scan_tx);
                p2p->after_scan_tx = NULL;
                return 1;
@@ -850,7 +895,8 @@ static void p2p_free_req_dev_types(struct p2p_data *p2p)
 
 int p2p_find(struct p2p_data *p2p, unsigned int timeout,
             enum p2p_discovery_type type,
-            unsigned int num_req_dev_types, const u8 *req_dev_types)
+            unsigned int num_req_dev_types, const u8 *req_dev_types,
+            const u8 *dev_id)
 {
        int res;
 
@@ -872,6 +918,12 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
                p2p->num_req_dev_types = num_req_dev_types;
        }
 
+       if (dev_id) {
+               os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN);
+               p2p->find_dev_id = p2p->find_dev_id_buf;
+       } else
+               p2p->find_dev_id = NULL;
+
        p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
        p2p_clear_timeout(p2p);
        p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
@@ -879,6 +931,7 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
        p2p_device_clear_reported(p2p);
        p2p_set_state(p2p, P2P_SEARCH);
        eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+       p2p->last_p2p_find_timeout = timeout;
        if (timeout)
                eloop_register_timeout(timeout, 0, p2p_find_timeout,
                                       p2p, NULL);
@@ -887,12 +940,12 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
        case P2P_FIND_PROGRESSIVE:
                res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
                                         p2p->num_req_dev_types,
-                                        p2p->req_dev_types);
+                                        p2p->req_dev_types, dev_id);
                break;
        case P2P_FIND_ONLY_SOCIAL:
                res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0,
                                         p2p->num_req_dev_types,
-                                        p2p->req_dev_types);
+                                        p2p->req_dev_types, dev_id);
                break;
        default:
                return -1;
@@ -904,6 +957,13 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
                eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
                eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
                                       p2p, NULL);
+       } else if (res == 1) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start "
+                       "p2p_scan at this point - will try again after "
+                       "previous scan completes");
+               res = 0;
+               p2p_set_state(p2p, P2P_SEARCH_WHEN_READY);
+               eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
        } else {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
                        "p2p_scan");
@@ -915,6 +975,20 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
 }
 
 
+int p2p_other_scan_completed(struct p2p_data *p2p)
+{
+       if (p2p->state != P2P_SEARCH_WHEN_READY)
+               return 0;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find "
+               "now that previous scan was completed");
+       if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type,
+                    p2p->num_req_dev_types, p2p->req_dev_types,
+                    p2p->find_dev_id) < 0)
+               return 0;
+       return 1;
+}
+
+
 void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
 {
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find");
@@ -1030,6 +1104,26 @@ static int p2p_prepare_channel(struct p2p_data *p2p, unsigned int force_freq)
 }
 
 
+static void p2p_set_dev_persistent(struct p2p_device *dev,
+                                  int persistent_group)
+{
+       switch (persistent_group) {
+       case 0:
+               dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP |
+                               P2P_DEV_PREFER_PERSISTENT_RECONN);
+               break;
+       case 1:
+               dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
+               dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN;
+               break;
+       case 2:
+               dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP |
+                       P2P_DEV_PREFER_PERSISTENT_RECONN;
+               break;
+       }
+}
+
+
 int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
                enum p2p_wps_method wps_method,
                int go_intent, const u8 *own_interface_addr,
@@ -1047,6 +1141,7 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
        if (p2p_prepare_channel(p2p, force_freq) < 0)
                return -1;
 
+       p2p->ssid_set = 0;
        dev = p2p_get_device(p2p, peer_addr);
        if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -1086,10 +1181,7 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
        dev->connect_reqs = 0;
        dev->go_neg_req_sent = 0;
        dev->go_state = UNKNOWN_GO;
-       if (persistent_group)
-               dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
-       else
-               dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_GROUP;
+       p2p_set_dev_persistent(dev, persistent_group);
        p2p->go_intent = go_intent;
        os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
 
@@ -1159,10 +1251,7 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
        dev->flags &= ~P2P_DEV_USER_REJECTED;
        dev->go_neg_req_sent = 0;
        dev->go_state = UNKNOWN_GO;
-       if (persistent_group)
-               dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
-       else
-               dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_GROUP;
+       p2p_set_dev_persistent(dev, persistent_group);
        p2p->go_intent = go_intent;
        os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
 
@@ -1273,8 +1362,12 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
        os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
        os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
        res.wps_method = peer->wps_method;
-       if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP)
-               res.persistent_group = 1;
+       if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+               if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+                       res.persistent_group = 2;
+               else
+                       res.persistent_group = 1;
+       }
 
        if (go) {
                /* Setup AP mode for WPS provisioning */
@@ -1314,6 +1407,7 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
        res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout;
 
        p2p_clear_timeout(p2p);
+       p2p->ssid_set = 0;
        peer->go_neg_req_sent = 0;
        peer->wps_method = WPS_NOT_READY;
 
@@ -1370,9 +1464,9 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
 }
 
 
-void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, const u8 *sa,
-                         const u8 *bssid, const u8 *data, size_t len,
-                         int freq)
+static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da,
+                                const u8 *sa, const u8 *bssid, const u8 *data,
+                                size_t len, int freq)
 {
        if (len < 1)
                return;
@@ -1517,6 +1611,7 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
        if (dev) {
                if (dev->country[0] == 0 && msg.listen_channel)
                        os_memcpy(dev->country, msg.listen_channel, 3);
+               os_get_time(&dev->last_seen);
                p2p_parse_free(&msg);
                return; /* already known */
        }
@@ -2288,11 +2383,11 @@ void p2p_continue_find(struct p2p_data *p2p)
                } else if (dev->req_config_methods &&
                           !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
                        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
-                               "pending Provisioning Discovery Request to "
+                               "pending Provision Discovery Request to "
                                MACSTR " (config methods 0x%x)",
                                MAC2STR(dev->info.p2p_device_addr),
                                dev->req_config_methods);
-                       if (p2p_send_prov_disc_req(p2p, dev, 0) == 0)
+                       if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0)
                                return;
                }
        }
@@ -2334,7 +2429,7 @@ static void p2p_sd_cb(struct p2p_data *p2p, int success)
  * 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)
+static void p2p_retry_pd(struct p2p_data *p2p)
 {
        struct p2p_device *dev;
 
@@ -2357,11 +2452,11 @@ void p2p_retry_pd(struct p2p_data *p2p)
                        continue;
 
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
-                       "pending Provisioning Discovery Request to "
+                       "pending Provision 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);
+               p2p_send_prov_disc_req(p2p, dev, 0, 0);
                return;
        }
 }
@@ -2446,10 +2541,12 @@ void p2p_scan_res_handled(struct p2p_data *p2p)
 }
 
 
-void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies)
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
 {
        u8 *len = p2p_buf_add_ie_hdr(ies);
        p2p_buf_add_capability(ies, p2p->dev_capab, 0);
+       if (dev_id)
+               p2p_buf_add_device_id(ies, dev_id);
        if (p2p->cfg->reg_class && p2p->cfg->channel)
                p2p_buf_add_listen_channel(ies, p2p->cfg->country,
                                           p2p->cfg->reg_class,
@@ -2462,6 +2559,12 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies)
 }
 
 
+size_t p2p_scan_ie_buf_len(struct p2p_data *p2p)
+{
+       return 100;
+}
+
+
 int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end)
 {
        return p2p_attr_text(p2p_ie, buf, end);
@@ -2741,7 +2844,7 @@ static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
         * state once per second to give other uses a chance to use the radio.
         */
        p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
-       p2p_set_timeout(p2p, 1, 0);
+       p2p_set_timeout(p2p, 0, 500000);
 }
 
 
@@ -2921,6 +3024,8 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
        case P2P_INVITE_LISTEN:
                p2p_timeout_invite_listen(p2p);
                break;
+       case P2P_SEARCH_WHEN_READY:
+               break;
        }
 }
 
@@ -2943,7 +3048,7 @@ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
 }
 
 
-static const char * p2p_wps_method_text(enum p2p_wps_method method)
+const char * p2p_wps_method_text(enum p2p_wps_method method)
 {
        switch (method) {
        case WPS_NOT_READY:
@@ -2975,14 +3080,10 @@ static const char * p2p_go_state_text(enum p2p_go_state go_state)
 }
 
 
-int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
-                     char *buf, size_t buflen)
+const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
+                                              const u8 *addr, int next)
 {
        struct p2p_device *dev;
-       int res;
-       char *pos, *end;
-       struct os_time now;
-       char devtype[WPS_DEV_TYPE_BUFSIZE];
 
        if (addr)
                dev = p2p_get_device(p2p, addr);
@@ -2996,35 +3097,37 @@ int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
        }
 
        if (dev == NULL)
+               return NULL;
+
+       return &dev->info;
+}
+
+
+int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
+                         char *buf, size_t buflen)
+{
+       struct p2p_device *dev;
+       int res;
+       char *pos, *end;
+       struct os_time now;
+
+       if (info == NULL)
                return -1;
 
+       dev = (struct p2p_device *) (((u8 *) info) -
+                                    offsetof(struct p2p_device, info));
+
        pos = buf;
        end = buf + buflen;
 
-       res = os_snprintf(pos, end - pos, MACSTR "\n",
-                         MAC2STR(dev->info.p2p_device_addr));
-       if (res < 0 || res >= end - pos)
-               return pos - buf;
-       pos += res;
-
        os_get_time(&now);
        res = os_snprintf(pos, end - pos,
                          "age=%d\n"
                          "listen_freq=%d\n"
-                         "level=%d\n"
                          "wps_method=%s\n"
                          "interface_addr=" MACSTR "\n"
                          "member_in_go_dev=" MACSTR "\n"
                          "member_in_go_iface=" MACSTR "\n"
-                         "pri_dev_type=%s\n"
-                         "device_name=%s\n"
-                         "manufacturer=%s\n"
-                         "model_name=%s\n"
-                         "model_number=%s\n"
-                         "serial_number=%s\n"
-                         "config_methods=0x%x\n"
-                         "dev_capab=0x%x\n"
-                         "group_capab=0x%x\n"
                          "go_neg_req_sent=%d\n"
                          "go_state=%s\n"
                          "dialog_token=%u\n"
@@ -3038,21 +3141,10 @@ int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
                          "invitation_reqs=%u\n",
                          (int) (now.sec - dev->last_seen.sec),
                          dev->listen_freq,
-                         dev->info.level,
                          p2p_wps_method_text(dev->wps_method),
                          MAC2STR(dev->interface_addr),
                          MAC2STR(dev->member_in_go_dev),
                          MAC2STR(dev->member_in_go_iface),
-                         wps_dev_type_bin2str(dev->info.pri_dev_type,
-                                              devtype, sizeof(devtype)),
-                         dev->info.device_name,
-                         dev->info.manufacturer,
-                         dev->info.model_name,
-                         dev->info.model_number,
-                         dev->info.serial_number,
-                         dev->info.config_methods,
-                         dev->info.dev_capab,
-                         dev->info.group_capab,
                          dev->go_neg_req_sent,
                          p2p_go_state_text(dev->go_state),
                          dev->dialog_token,
@@ -3121,6 +3213,12 @@ int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
 }
 
 
+int p2p_peer_known(struct p2p_data *p2p, const u8 *addr)
+{
+       return p2p_get_device(p2p, addr) != NULL;
+}
+
+
 void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled)
 {
        if (enabled) {
@@ -3681,3 +3779,11 @@ p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next)
 
        return &dev->info;
 }
+
+
+int p2p_in_progress(struct p2p_data *p2p)
+{
+       if (p2p == NULL)
+               return 0;
+       return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
+}