]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
P2P: Add mechanism for timing out idle groups
authorJouni Malinen <jouni.malinen@atheros.com>
Mon, 25 Oct 2010 15:24:15 +0000 (18:24 +0300)
committerJouni Malinen <j@w1.fi>
Mon, 25 Oct 2010 15:24:15 +0000 (18:24 +0300)
A new configuration parameter, p2p_group_idle, can now be used to set
idle timeout value for P2P groups in seconds (0 = no timeout). If set,
this values is used to remove P2P group (both GO and P2P client)
interfaces after the group has been idle (no clients/GO seen) for the
configuration duration.

The P2P-GROUP-REMOVED event is now indicating the reason for group
removal when known. For example:
P2P-GROUP-REMOVED wlan0 GO reason=REQUESTED
P2P-GROUP-REMOVED wlan1 client reason=IDLE

src/p2p/p2p.h
src/p2p/p2p_group.c
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/config_winreg.c
wpa_supplicant/p2p_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index ffe02a2a89c9bfb98c0141bdcd240d917ba2a519..a4e5d62b945dcd3201660d817a873b72ee704048 100644 (file)
@@ -1049,6 +1049,13 @@ struct p2p_group_config {
         */
        void (*ie_update)(void *ctx, struct wpabuf *beacon_ies,
                          struct wpabuf *proberesp_ies);
+
+       /**
+        * idle_update - Notification of changes in group idle state
+        * @ctx: Callback context from cb_ctx
+        * @idle: Whether the group is idle (no associated stations)
+        */
+       void (*idle_update)(void *ctx, int idle);
 };
 
 /**
index 22a1ca73cb79e67a47cbd628ee8eb039965e876c..7d2588598403763715c5ca1add9dbd54bbf1ea52 100644 (file)
@@ -72,6 +72,7 @@ struct p2p_group * p2p_group_init(struct p2p_data *p2p,
        group->group_formation = 1;
        group->beacon_update = 1;
        p2p_group_update_ies(group);
+       group->cfg->idle_update(group->cfg->cb_ctx, 1);
 
        return group;
 }
@@ -338,6 +339,8 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
        if (group->num_members == group->cfg->max_clients)
                group->beacon_update = 1;
        p2p_group_update_ies(group);
+       if (group->num_members == 1)
+               group->cfg->idle_update(group->cfg->cb_ctx, 0);
 
        return 0;
 }
@@ -396,6 +399,8 @@ void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr)
                if (group->num_members == group->cfg->max_clients - 1)
                        group->beacon_update = 1;
                p2p_group_update_ies(group);
+               if (group->num_members == 0)
+                       group->cfg->idle_update(group->cfg->cb_ctx, 1);
        }
 }
 
index 24bbf0fdf7fba054cfcafe4729a8e8a00adec9ad..081f7e065deef1ad80d0699a05c5cac2158ff3fe 100644 (file)
@@ -2403,6 +2403,7 @@ static const struct global_parse_data global_fields[] = {
        { STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX },
        { INT_RANGE(persistent_reconnect, 0, 1), 0 },
        { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
+       { INT(p2p_group_idle), 0 },
 #endif /* CONFIG_P2P */
        { FUNC(country), CFG_CHANGED_COUNTRY },
        { INT(bss_max_count), 0 },
index ff4cf22de56c56e5d7dead1793518f2cf0aaa596..f288b2c976479dcfe75077fbc4d6fe694e4ac20c 100644 (file)
@@ -366,6 +366,18 @@ struct wpa_config {
        int persistent_reconnect;
        int p2p_intra_bss;
 
+       /**
+        * p2p_group_idle - Maximum idle time in seconds for P2P group
+        *
+        * This value controls how long a P2P group is maintained after there
+        * is no other members in the group. As a GO, this means no associated
+        * stations in the group. As a P2P client, this means no GO seen in
+        * scan results. The maximum idle time is specified in seconds with 0
+        * indicating no time limit, i.e., the P2P group remains in active
+        * state indefinitely until explicitly removed.
+        */
+       unsigned int p2p_group_idle;
+
        /**
         * bss_max_count - Maximum number of BSS entries to keep in memory
         */
index a203e4331cbc53647e0e3c943b3776f0b0b565b1..2431e84f35f57294845d3a8c456db5453fe35498 100644 (file)
@@ -675,7 +675,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
                        config->persistent_reconnect);
        if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS)
                fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
-
+       if (config->p2p_group_idle)
+               fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
 #endif /* CONFIG_P2P */
        if (config->country[0] && config->country[1]) {
                fprintf(f, "country=%c%c\n",
index b2078af4f85c2e9532b4b9eab555ea275ff52c9c..ef95a29453c523e0c3d948eaeb42c8a3381d5fe8 100644 (file)
@@ -259,6 +259,8 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk)
 #ifdef CONFIG_P2P
        config->p2p_ssid_postfix = wpa_config_read_reg_string(
                hk, TEXT("p2p_ssid_postfix"));
+       wpa_config_read_reg_dword(hk, TEXT("p2p_group_idle"),
+                                 (int *) &config->p2p_group_idle);
 #endif /* CONFIG_P2P */
 
        wpa_config_read_reg_dword(hk, TEXT("bss_max_count"),
@@ -596,6 +598,8 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
 #ifdef CONFIG_P2P
        wpa_config_write_reg_string(hk, "p2p_ssid_postfix",
                                    config->p2p_ssid_postfix);
+       wpa_config_write_reg_dword(hk, TEXT("p2p_group_idle"),
+                                  config->p2p_group_idle, 0);
 #endif /* CONFIG_P2P */
 
        wpa_config_write_reg_dword(hk, TEXT("bss_max_count"),
index ab298f2b0be847db19805833b97185ffb7c81da6..7a7524e87ee816fcc67fb4a8cb8cd9f7d79cda37 100644 (file)
@@ -46,6 +46,8 @@ static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
                         const u8 *dev_addr, enum p2p_wps_method wps_method);
 static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
 
 
 static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
@@ -175,6 +177,9 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
 {
        struct wpa_ssid *ssid;
        char *gtype;
+       const char *reason;
+
+       eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 
        ssid = wpa_s->current_ssid;
        if (ssid == NULL) {
@@ -208,8 +213,19 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
                        P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
                        wpa_s->ifname, wpa_s->cross_connect_uplink);
        }
-       wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s",
-               wpa_s->ifname, gtype);
+       switch (wpa_s->removal_reason) {
+       case P2P_GROUP_REMOVAL_REQUESTED:
+               reason = " reason=REQUESTED";
+               break;
+       case P2P_GROUP_REMOVAL_IDLE_TIMEOUT:
+               reason = " reason=IDLE";
+               break;
+       default:
+               reason = "";
+               break;
+       }
+       wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s",
+               wpa_s->ifname, gtype, reason);
        if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
                struct wpa_global *global;
                char *ifname;
@@ -451,6 +467,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
                        MAC2STR(go_dev_addr),
                        persistent ? " [PERSISTENT]" : "");
                wpas_p2p_cross_connect_setup(wpa_s);
+               wpas_p2p_set_group_idle_timeout(wpa_s);
        } else {
                wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
                        "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
@@ -460,6 +477,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
                        MAC2STR(go_dev_addr),
                        persistent ? " [PERSISTENT]" : "");
                wpas_p2p_cross_connect_setup(wpa_s);
+               wpas_p2p_set_group_idle_timeout(wpa_s);
        }
 
        if (persistent)
@@ -743,6 +761,7 @@ static void p2p_go_configured(void *ctx, void *data)
                                wpa_s->parent, ssid,
                                wpa_s->parent->own_addr);
                wpas_p2p_cross_connect_setup(wpa_s);
+               wpas_p2p_set_group_idle_timeout(wpa_s);
                return;
        }
 
@@ -822,6 +841,8 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
        C(device_type);
        C(config_methods);
 #undef C
+
+       d->p2p_group_idle = s->p2p_group_idle;
 }
 
 
@@ -2244,6 +2265,7 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
        eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
        wpa_s->p2p_long_listen = 0;
        eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+       eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
        wpas_p2p_remove_pending_group_interface(wpa_s);
 
        /* TODO: remove group interface from the driver if this wpa_s instance
@@ -2782,6 +2804,7 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
                while (wpa_s) {
                        prev = wpa_s;
                        wpa_s = wpa_s->next;
+                       wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
                        wpas_p2p_group_delete(prev);
                }
                return 0;
@@ -2795,6 +2818,7 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
        if (wpa_s == NULL)
                return -1;
 
+       wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
        wpas_p2p_group_delete(wpa_s);
 
        return 0;
@@ -3001,6 +3025,19 @@ static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies,
 }
 
 
+static void wpas_p2p_idle_update(void *ctx, int idle)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (!wpa_s->ap_iface)
+               return;
+       wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
+       if (idle)
+               wpas_p2p_set_group_idle_timeout(wpa_s);
+       else
+               eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+}
+
+
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
                                       int persistent_group,
                                       int group_formation)
@@ -3021,6 +3058,7 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
                cfg->max_clients = wpa_s->conf->max_num_sta;
        cfg->cb_ctx = wpa_s;
        cfg->ie_update = wpas_p2p_ie_update;
+       cfg->idle_update = wpas_p2p_idle_update;
 
        group = p2p_group_init(wpa_s->global->p2p, cfg);
        if (group == NULL)
@@ -3375,6 +3413,39 @@ int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
 }
 
 
+static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+
+       if (wpa_s->conf->p2p_group_idle == 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - "
+                          "disabled");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate "
+                  "group");
+       wpa_s->removal_reason = P2P_GROUP_REMOVAL_IDLE_TIMEOUT;
+       wpas_p2p_group_delete(wpa_s);
+}
+
+
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
+{
+       eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+       if (wpa_s->conf->p2p_group_idle == 0)
+               return;
+
+       if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
+               return;
+
+       wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds",
+                  wpa_s->conf->p2p_group_idle);
+       eloop_register_timeout(wpa_s->conf->p2p_group_idle, 0,
+                              wpas_p2p_group_idle_timeout, wpa_s, NULL);
+}
+
+
 void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
                           u16 reason_code, const u8 *ie, size_t ie_len)
 {
@@ -3557,12 +3628,16 @@ void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
                wpas_p2p_disable_cross_connect(wpa_s);
        else
                wpas_p2p_enable_cross_connect(wpa_s);
+       if (!wpa_s->ap_iface)
+               eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 }
 
 
 void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
 {
        wpas_p2p_disable_cross_connect(wpa_s);
+       if (!wpa_s->ap_iface)
+               wpas_p2p_set_group_idle_timeout(wpa_s);
 }
 
 
index 66f544e3a966e22fcf863f9f958995df5fee678b..f5e80bab0757d93578009eed33355785a8e3923e 100644 (file)
@@ -514,6 +514,12 @@ struct wpa_supplicant {
         * Uplink interface name for cross connection
         */
        char cross_connect_uplink[100];
+
+       enum {
+               P2P_GROUP_REMOVAL_UNKNOWN,
+               P2P_GROUP_REMOVAL_REQUESTED,
+               P2P_GROUP_REMOVAL_IDLE_TIMEOUT
+       } removal_reason;
 #endif /* CONFIG_P2P */
 
        struct wpa_ssid *bgscan_ssid;