P2P: Share code for p2p_connect/authorize channel preparation
[libeap.git] / src / p2p / p2p.c
index 9fc6abf..122c121 100644 (file)
@@ -419,10 +419,12 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
  * Returns: 0 on success, -1 on failure
  *
  * If the scan result is for a GO, the clients in the group will also be added
- * to the peer table.
+ * to the peer table. This function can also be used with some other frames
+ * like Provision Discovery Request that contains P2P Capability and P2P Device
+ * Info attributes.
  */
-static int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
-                         int level, const u8 *ies, size_t ies_len)
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
+                  const u8 *ies, size_t ies_len)
 {
        struct p2p_device *dev;
        struct p2p_message msg;
@@ -498,6 +500,8 @@ static int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
                        msg.ds_params ? *msg.ds_params : -1);
        }
        dev->listen_freq = freq;
+       if (msg.group_info)
+               dev->oper_freq = freq;
        dev->level = level;
 
        if (msg.pri_dev_type)
@@ -793,29 +797,27 @@ void p2p_stop_find(struct p2p_data *p2p)
 }
 
 
-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,
-               unsigned int force_freq, int persistent_group)
+static int p2p_prepare_channel(struct p2p_data *p2p, unsigned int force_freq)
 {
-       struct p2p_device *dev;
-
-       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-               "P2P: Request to start group negotiation - peer=" MACSTR
-               "  GO Intent=%d  Intended Interface Address=" MACSTR
-               " wps_method=%d persistent_group=%d",
-               MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-               wps_method, persistent_group);
-
        if (force_freq) {
+               u8 op_reg_class, op_channel;
                if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
-                                       &p2p->op_reg_class, &p2p->op_channel) <
-                   0) {
+                                       &op_reg_class, &op_channel) < 0) {
                        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                                "P2P: Unsupported frequency %u MHz",
                                force_freq);
                        return -1;
                }
+               if (!p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+                                          op_channel)) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Frequency %u MHz (oper_class %u "
+                               "channel %u) not allowed for P2P",
+                               force_freq, op_reg_class, op_channel);
+                       return -1;
+               }
+               p2p->op_reg_class = op_reg_class;
+               p2p->op_channel = op_channel;
                p2p->channels.reg_classes = 1;
                p2p->channels.reg_class[0].channels = 1;
                p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
@@ -832,6 +834,27 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
                p2p->op_reg_class, p2p->op_channel,
                force_freq ? " (forced)" : "");
 
+       return 0;
+}
+
+
+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,
+               unsigned int force_freq, int persistent_group)
+{
+       struct p2p_device *dev;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Request to start group negotiation - peer=" MACSTR
+               "  GO Intent=%d  Intended Interface Address=" MACSTR
+               " wps_method=%d persistent_group=%d",
+               MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+               wps_method, persistent_group);
+
+       if (p2p_prepare_channel(p2p, force_freq) < 0)
+               return -1;
+
        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,
@@ -914,30 +937,8 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
                MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
                wps_method, persistent_group);
 
-       if (force_freq) {
-               if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
-                                       &p2p->op_reg_class, &p2p->op_channel) <
-                   0) {
-                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-                               "P2P: Unsupported frequency %u MHz",
-                               force_freq);
-                       return -1;
-               }
-               p2p->channels.reg_classes = 1;
-               p2p->channels.reg_class[0].channels = 1;
-               p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
-               p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
-       } else {
-               p2p->op_reg_class = p2p->cfg->op_reg_class;
-               p2p->op_channel = p2p->cfg->op_channel;
-               os_memcpy(&p2p->channels, &p2p->cfg->channels,
-                         sizeof(struct p2p_channels));
-       }
-       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-               "P2P: Own preference for operation channel: "
-               "Regulatory Class %u Channel %u%s",
-               p2p->op_reg_class, p2p->op_channel,
-               force_freq ? " (forced)" : "");
+       if (p2p_prepare_channel(p2p, force_freq) < 0)
+               return -1;
 
        dev = p2p_get_device(p2p, peer_addr);
        if (dev == NULL) {
@@ -1090,8 +1091,13 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
                os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
                res.ssid_len = p2p->ssid_len;
                p2p_random(res.passphrase, 8);
-       } else
+       } else {
                res.freq = peer->oper_freq;
+               if (p2p->ssid_len) {
+                       os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
+                       res.ssid_len = p2p->ssid_len;
+               }
+       }
 
        p2p_channels_intersect(&p2p->channels, &peer->channels,
                               &intersection);
@@ -1112,6 +1118,8 @@ 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);
        peer->go_neg_req_sent = 0;
        peer->wps_method = WPS_NOT_READY;
@@ -1612,6 +1620,7 @@ static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid,
        u8 *lpos;
        size_t tmplen;
        int res;
+       u8 group_capab;
 
        if (p2p_ie == NULL)
                return 0; /* WLAN AP is not a P2P manager */
@@ -1627,7 +1636,15 @@ static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid,
                return -1;
 
        lpos = p2p_buf_add_ie_hdr(tmp);
-       p2p_buf_add_capability(tmp, p2p->dev_capab, 0);
+       group_capab = 0;
+       if (p2p->num_groups > 0) {
+               group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
+               if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
+                   (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) &&
+                   p2p->cross_connect)
+                       group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+       }
+       p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab);
        if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
            (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED))
                p2p_buf_add_p2p_interface(tmp, p2p);
@@ -2052,6 +2069,10 @@ static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success)
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                "P2P: GO Negotiation Response (failure) TX callback: "
                "success=%d", success);
+       if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) {
+               p2p_go_neg_failed(p2p, p2p->go_neg_peer,
+                                 p2p->go_neg_peer->status);
+       }
 }
 
 
@@ -2796,6 +2817,20 @@ static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx)
                                       p2p_ext_listen_timeout, p2p, NULL);
        }
 
+       if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) {
+               /*
+                * This should not really happen, but it looks like the Listen
+                * command may fail is something else (e.g., a scan) was
+                * running at an inconvenient time. As a workaround, allow new
+                * Extended Listen operation to be started.
+                */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Previous "
+                       "Extended Listen operation had not been completed - "
+                       "try again");
+               p2p->ext_listen_only = 0;
+               p2p_set_state(p2p, P2P_IDLE);
+       }
+
        if (p2p->state != P2P_IDLE) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended "
                        "Listen timeout in active state (%s)",
@@ -2950,6 +2985,17 @@ int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
 }
 
 
+int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
+                          u8 *dev_addr)
+{
+       struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
+       if (dev == NULL)
+               return -1;
+       os_memcpy(dev_addr, dev->p2p_device_addr, ETH_ALEN);
+       return 0;
+}
+
+
 void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr)
 {
        os_memcpy(p2p->peer_filter, addr, ETH_ALEN);
@@ -2960,3 +3006,33 @@ void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr)
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer "
                        "filter for " MACSTR, MAC2STR(p2p->peer_filter));
 }
+
+
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Cross connection %s",
+               enabled ? "enabled" : "disabled");
+       if (p2p->cross_connect == enabled)
+               return;
+       p2p->cross_connect = enabled;
+       /* TODO: may need to tear down any action group where we are GO(?) */
+}
+
+
+int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr)
+{
+       struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
+       if (dev == NULL)
+               return -1;
+       if (dev->oper_freq <= 0)
+               return -1;
+       return dev->oper_freq;
+}
+
+
+void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Intra BSS distribution %s",
+               enabled ? "enabled" : "disabled");
+       p2p->cfg->p2p_intra_bss = enabled;
+}