Send connman mailing list submissions to
        connman@lists.01.org

To subscribe or unsubscribe via the World Wide Web, visit
        https://lists.01.org/mailman/listinfo/connman
or, via email, send a message with subject or body 'help' to
        connman-requ...@lists.01.org

You can reach the person managing the list at
        connman-ow...@lists.01.org

When replying, please edit your Subject line so it is more specific
than "Re: Contents of connman digest..."


Today's Topics:

   1. [PATCH] add WPS patch (natsuki.it...@sony.com)


----------------------------------------------------------------------

Message: 1
Date: Fri, 6 Jul 2018 10:21:00 +0000
From: <natsuki.it...@sony.com>
To: <connman@lists.01.org>
Subject: [PATCH] add WPS patch
Message-ID:
        <7a4e6fce7871824e939cd5043f4016402ad00...@jpyokxms103.jp.sony.com>
Content-Type: text/plain; charset="us-ascii"

Hi,

As ConnMan community previously discussed so, the WPS function has some issue.
https://lists.01.org/pipermail/connman/2018-January/022367.html

In short, following technical issues / failure cases have been identified; 
- Cannot start WPS protocol without specifying the target AP.
- WPS protocol sometimes fails with 
  + the AP that distributes multi-credentials
  + Multi-band AP
  + "Unconfigured" AP
Due to above issues, any products which uses ConnMan cannot pass WPS 
certification test.

Please find the attached patch (based on v1.36) for further details.
We have used this patch for several months and have not seen any negative side 
effects.

To fix issues, we've separated current WPS protocol procedure into
a "credential distribution" and a "connection" processes.

Proposed WPS API do only receive credential(s) delivered by WPS protocol.
The connman notify service(s) that receive WPS credential, an upper layer 
application, 
like the connmanctl, must call "connect API" to the service.

Please take a look at proposed patch, and give us your opinion 
so that we can get an advice on how we should proceed to contribute this patch 
into ConnMan official codebase for future releases.

---
 client/commands.c         | 127 ++++++++
 client/dbus_helpers.c     |   2 +-
 doc/agent-api.txt         |   6 +
 doc/technology-api.txt    |  43 +++
 gsupplicant/gsupplicant.h |  32 ++
 gsupplicant/supplicant.c  | 479 +++++++++++++++++++++------
 include/technology.h      |  20 ++
 plugins/wifi.c            | 666 ++++++++++++++++++++++++++++++++++++--
 src/connman.h             |   6 +
 src/error.c               |  16 +
 src/peer.c                |   5 +-
 src/service.c             |  21 ++
 src/technology.c          | 224 +++++++++++++
 13 files changed, 1522 insertions(+), 125 deletions(-)

diff --git a/client/commands.c b/client/commands.c
index 05b93162..1316ddbb 100644
--- a/client/commands.c
+++ b/client/commands.c
@@ -639,6 +639,87 @@ static int cmd_tether(char *args[], int num, struct 
connman_option *options)
        return tether_set(args[1], set_tethering);
 }
 
+static int wps_cb(DBusMessageIter *iter, const char *error,
+               void *user_data)
+{
+       bool sta = GPOINTER_TO_UINT(user_data);
+
+       if (error) {
+               fprintf(stderr, "WPS Error: %s\n", error);
+               return 1;
+       }
+
+       if (sta) {
+               fprintf(stdout, "WPS: Success\n");
+               services_list(iter, NULL, NULL);
+       }
+       else
+               fprintf(stdout, "WPS: Started\n");
+
+       return 0;
+}
+
+static void set_wps_authentication(DBusMessageIter *iter, void *user_data)
+{
+       const char *auth = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &auth);
+
+       return;
+}
+
+static int cmd_wps_start(char *args[], int num, struct connman_option *options)
+{
+       char *auth = "";
+       char *method = "Start_STA_WPS";
+       bool sta = true;
+
+       if (num > 3)
+               return -E2BIG;
+
+       if (num < 3)
+               return -EINVAL;
+
+       if (g_strcmp0(args[2], "pbc") != 0)
+               auth = args[2];
+
+       if (g_strcmp0(args[1], "ap") == 0) {
+               method = "Start_AP_WPS";
+               sta = false;
+       } else if (g_strcmp0(args[1], "sta") != 0) {
+               fprintf(stdout, "Incorrect mode %s. Correct options: ap "
+                                                       " or sta\n", args[1]);
+               return -EINVAL;
+       }
+
+       return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
+                                       "/net/connman/technology/wifi", 
"net.connman.Technology",
+                                       method, wps_cb, GUINT_TO_POINTER(sta),
+                                       set_wps_authentication, auth);
+}
+
+static int wps_cancel_cb(DBusMessageIter *iter, const char *error,
+               void *user_data)
+{
+       if (!error) {
+               fprintf(stdout, "Ongoing WPS session cancelled\n");
+       } else
+               fprintf(stdout, "WPS Error: %s\n", error);
+
+       return 0;
+}
+
+static int cmd_wps_cancel(char *args[], int num, struct connman_option 
*options)
+{
+       if (num > 1)
+               return -E2BIG;
+
+       return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
+                               "/net/connman/technology/wifi",
+                               "net.connman.Technology", "Cancel_WPS",
+                               wps_cancel_cb, NULL, NULL, NULL);
+}
+
 static int scan_return(DBusMessageIter *iter, const char *error,
                void *user_data)
 {
@@ -2332,6 +2413,46 @@ static char *lookup_tether(const char *text, int state)
        return NULL;
 }
 
+static char *lookup_wps_option(const char *text, int state, char **opt)
+{
+       static int idx = 0;
+       static int len = 0;
+       char *str;
+
+       if (!state) {
+               idx = 0;
+               len = strlen(text);
+       }
+
+       while (opt[idx]) {
+               str = opt[idx];
+               idx++;
+
+               if (!strncmp(text, str, len))
+                       return strdup(str);
+       }
+
+       return NULL;
+}
+
+static char *lookup_wps(const char *text, int state)
+{
+       int level;
+       char *action[] = { "ap", "sta", NULL };
+       char *type[] = { "pbc", NULL };
+
+       level = __connmanctl_input_calc_level();
+
+       if (level == 1)
+               return lookup_wps_option(text, state, action);
+       if (level == 2)
+               return lookup_wps_option(text, state, type);
+
+       __connmanctl_input_lookup_end();
+
+       return NULL;
+}
+
 static char *lookup_agent(const char *text, int state)
 {
        if (__connmanctl_input_calc_level() > 1) {
@@ -2736,6 +2857,12 @@ static const struct {
                                          NULL,            cmd_tether,
          "Enable, disable tethering, set SSID and passphrase for wifi",
          lookup_tether },
+       { "wps", "ap|sta <pbc/PIN>",          NULL,        cmd_wps_start,
+         "Start WPS in AP or STA mode with PBC or indicating the PIN to be 
used",
+         lookup_wps },
+       { "wps_cancel",   NULL,           NULL,            cmd_wps_cancel,
+         "Cancel ongoing WPS Session",
+         lookup_wps },
        { "services",     "[<service>]",  service_options, cmd_services,
          "Display services", lookup_service_arg },
        { "peers",        "[peer]",       NULL,            cmd_peers,
diff --git a/client/dbus_helpers.c b/client/dbus_helpers.c
index 6ca407d4..1a8eea16 100644
--- a/client/dbus_helpers.c
+++ b/client/dbus_helpers.c
@@ -28,7 +28,7 @@
 #include "input.h"
 #include "dbus_helpers.h"
 
-#define TIMEOUT         120000
+#define TIMEOUT         125000
 
 void __connmanctl_dbus_print(DBusMessageIter *iter, const char *pre,
                const char *dict, const char *sep)
diff --git a/doc/agent-api.txt b/doc/agent-api.txt
index e3c1dcde..24b7f762 100644
--- a/doc/agent-api.txt
+++ b/doc/agent-api.txt
@@ -141,6 +141,12 @@ Fields             string Name
                        In case of a RequestPeerAuthorization, this field will
                        be set as mandatory.
 
+                       To use this in order to get connected to a given
+                       services is deprecated and should not be used. Methods
+                       Technology.Start_STA_WPS and Technology.Start_AP_WPS
+                       are provided by ConnMan and user should use those two
+                       instead.
+
                string Username
 
                        Username for WISPr authentication. This field will be
diff --git a/doc/technology-api.txt b/doc/technology-api.txt
index f22e9b29..75c1091d 100644
--- a/doc/technology-api.txt
+++ b/doc/technology-api.txt
@@ -40,6 +40,49 @@ Methods              dict GetProperties()  [deprecated]
                        via the PeersChanged signal from the manager
                        interface.
 
+               void Start_AP_WPS(string authentication)
+
+                       Start a WPS Session when the system is playing AP
+                       role (Tethering) in order to allow potential Ex-STAs
+                       to get connected by using WPS.
+
+                       The argument indicates the WPS authentication method
+                       which can be an empty string, if user wants to use
+                       push-button method, or a pin code if user wants to
+                       use the pin method.
+
+                       This method is supported only by WiFi technology.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.PermissionDenied
+                                        [service].Error.NotSupported
+
+               void Start_STA_WPS(string authentication)
+
+                       Start a WPS Session in STA role in order to be able
+                       to get connected with an Ex-AP with WPS capabilities.
+
+                       The argument indicates the WPS authentication method
+                       which can be an empty string, if user wants to use
+                       push-button method, or a pin code if user wants to
+                       use the pin method.
+
+                       This method is supported only by WiFi technology.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.OperationAborted
+                                        [service].Error.OperationTimeout
+                                        [service].Error.NotConnected
+                                        [service].Error.NotSupported
+
+               void Cancel_WPS()
+
+                       Cancel ongoing WPS session.
+
+                       This method is supported only by WiFi technology.
+
+                       Possible Errors: [service].Error.NotSupported
+
 Signals                PropertyChanged(string name, variant value)
 
                        This signal indicates a changed value of the given
diff --git a/gsupplicant/gsupplicant.h b/gsupplicant/gsupplicant.h
index db61595b..95388fdd 100644
--- a/gsupplicant/gsupplicant.h
+++ b/gsupplicant/gsupplicant.h
@@ -84,6 +84,18 @@ extern "C" {
 #define G_SUPPLICANT_GROUP_ROLE_CLIENT (1 << 0)
 #define G_SUPPLICANT_GROUP_ROLE_GO      (1 << 1)
 
+#define G_SUPPLICANT_WPS_AUTH_OPEN             (1 << 0)
+#define G_SUPPLICANT_WPS_AUTH_WPA_PSK  (1 << 1)
+#define G_SUPPLICANT_WPS_AUTH_SHARED   (1 << 2) /* Depricated */
+#define G_SUPPLICANT_WPS_AUTH_WPA_EAP  (1 << 3)
+#define G_SUPPLICANT_WPS_AUTH_WPA2_EAP (1 << 4)
+#define G_SUPPLICANT_WPS_AUTH_WPA2_PSK (1 << 5)
+
+#define G_SUPPLICANT_WPS_ENCR_NONE             (1 << 0)
+#define G_SUPPLICANT_WPS_ENCR_WEP              (1 << 1)
+#define G_SUPPLICANT_WPS_ENCR_TKIP             (1 << 2)
+#define G_SUPPLICANT_WPS_ENCR_AES              (1 << 3)
+
 typedef enum {
        G_SUPPLICANT_MODE_UNKNOWN,
        G_SUPPLICANT_MODE_INFRA,
@@ -159,6 +171,13 @@ struct _GSupplicantSSID {
 
 typedef struct _GSupplicantSSID GSupplicantSSID;
 
+struct _GSupplicantWPSParams {
+       GSupplicantMode mode;
+       const char *pin;
+};
+
+typedef struct _GSupplicantWPSParams GSupplicantWPSParams;
+
 struct scan_ssid {
        unsigned char ssid[32];
        uint8_t ssid_len;
@@ -259,6 +278,14 @@ int g_supplicant_set_widi_ies(GSupplicantP2PServiceParams 
*p2p_service_params,
                                        GSupplicantInterfaceCallback callback,
                                        void *user_data);
 
+int g_supplicant_interface_wps_cancel(GSupplicantInterface *interface,
+                                       GSupplicantInterfaceCallback callback, 
void * user_data);
+
+int g_supplicant_interface_wps_start(GSupplicantInterface *interface,
+                                       GSupplicantWPSParams *wps,
+                                       GSupplicantInterfaceCallback callback,
+                                       void *user_data);
+
 int g_supplicant_interface_connect(GSupplicantInterface *interface,
                                        GSupplicantSSID *ssid,
                                        GSupplicantInterfaceCallback callback,
@@ -292,6 +319,7 @@ int g_supplicant_interface_set_country(GSupplicantInterface 
*interface,
                                        GSupplicantCountryCallback callback,
                                                        const char *alpha2,
                                                        void *user_data);
+bool g_supplicant_interface_support_wps(GSupplicantInterface *interface);
 bool g_supplicant_interface_has_p2p(GSupplicantInterface *interface);
 int g_supplicant_interface_set_p2p_device_config(GSupplicantInterface 
*interface,
                                                const char *device_name,
@@ -299,6 +327,7 @@ int 
g_supplicant_interface_set_p2p_device_config(GSupplicantInterface *interface
 GSupplicantPeer *g_supplicant_interface_peer_lookup(GSupplicantInterface 
*interface,
                                                const char *identifier);
 bool g_supplicant_interface_is_p2p_finding(GSupplicantInterface *interface);
+void g_supplicant_interface_flushBSS(GSupplicantInterface *interface, unsigned 
int age);
 
 /* Network and Peer API */
 struct _GSupplicantNetwork;
@@ -352,6 +381,8 @@ struct _GSupplicantCallbacks {
        void (*network_changed) (GSupplicantNetwork *network,
                                        const char *property);
        void (*network_associated) (GSupplicantNetwork *network);
+       void (*wps_event) (GSupplicantInterface *interface,
+                                       GSupplicantWpsState event, int error);
        void (*peer_found) (GSupplicantPeer *peer);
        void (*peer_lost) (GSupplicantPeer *peer);
        void (*peer_changed) (GSupplicantPeer *peer,
@@ -362,6 +393,7 @@ struct _GSupplicantCallbacks {
                                int reasoncode);
        void (*assoc_status_code)(GSupplicantInterface *interface,
                                int reasoncode);
+       void (*wps_credential)(GSupplicantInterface *interface, const char 
*group);
 };
 
 typedef struct _GSupplicantCallbacks GSupplicantCallbacks;
diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c
index 5246c80b..6fcc2d23 100644
--- a/gsupplicant/supplicant.c
+++ b/gsupplicant/supplicant.c
@@ -138,6 +138,24 @@ static struct strvalmap mode_capa_map[] = {
        { }
 };
 
+static struct strvalmap wps_auth_map[] = {
+       { "open",               G_SUPPLICANT_WPS_AUTH_OPEN              },
+       { "shared",             G_SUPPLICANT_WPS_AUTH_SHARED    },
+       { "wpa-psk",    G_SUPPLICANT_WPS_AUTH_WPA_PSK   },
+       { "wpa-eap",    G_SUPPLICANT_WPS_AUTH_WPA_EAP   },
+       { "wpa2-eap",   G_SUPPLICANT_WPS_AUTH_WPA2_EAP  },
+       { "wpa2-psk",   G_SUPPLICANT_WPS_AUTH_WPA2_PSK  },
+       { }
+};
+
+static struct strvalmap wps_encr_map[] = {
+       { "none",               G_SUPPLICANT_WPS_ENCR_NONE              },
+       { "wep",                G_SUPPLICANT_WPS_ENCR_WEP               },
+       { "tkip",               G_SUPPLICANT_WPS_ENCR_TKIP              },
+       { "aes",                G_SUPPLICANT_WPS_ENCR_AES               },
+       { }
+};
+
 static GHashTable *interface_table;
 static GHashTable *bss_mapping;
 static GHashTable *peer_mapping;
@@ -146,9 +164,14 @@ static GHashTable *pending_peer_connection;
 static GHashTable *config_file_table;
 
 struct _GSupplicantWpsCredentials {
+       unsigned char bssid[6];
        unsigned char ssid[32];
        unsigned int ssid_len;
+
        char *key;
+
+       unsigned int authtype;
+       unsigned int encrtype;
 };
 
 struct added_network_information {
@@ -181,6 +204,7 @@ struct _GSupplicantInterface {
        char *ifname;
        char *driver;
        char *bridge;
+       bool wps_support;
        struct _GSupplicantWpsCredentials wps_cred;
        GSupplicantWpsState wps_state;
        GHashTable *network_table;
@@ -287,6 +311,7 @@ struct interface_connect_data {
        union {
                GSupplicantSSID *ssid;
                GSupplicantPeerParams *peer;
+               GSupplicantWPSParams *wps;
        };
 };
 
@@ -298,6 +323,12 @@ struct interface_scan_data {
        void *user_data;
 };
 
+struct wps_event_args {
+       int msg; /* enum wps_msg_type */
+       int config_error; /* enum wps_config_error */
+       int error_indication; /* enum wps_error_indication */
+};
+
 static int network_remove(struct interface_data *data);
 
 static inline void debug(const char *format, ...)
@@ -614,6 +645,19 @@ static void callback_network_associated(GSupplicantNetwork 
*network)
        callbacks_pointer->network_associated(network);
 }
 
+static void callback_wps_event(GSupplicantInterface *interface,
+                                       GSupplicantWpsState state, int error)
+{
+       SUPPLICANT_DBG("");
+       if (!callbacks_pointer)
+               return;
+
+       if (!callbacks_pointer->wps_event)
+               return;
+
+       callbacks_pointer->wps_event(interface, state, error);
+}
+
 static void callback_peer_found(GSupplicantPeer *peer)
 {
        if (!callbacks_pointer)
@@ -688,6 +732,18 @@ static void 
callback_assoc_status_code(GSupplicantInterface *interface,
 
 }
 
+static void callback_wps_credential(GSupplicantInterface *interface,
+                                                                       const 
char *group)
+{
+       if (!callbacks_pointer)
+               return;
+
+       if (!callbacks_pointer->assoc_status_code)
+               return;
+
+       callbacks_pointer->wps_credential(interface, group);
+}
+
 static void remove_group(gpointer data)
 {
        GSupplicantGroup *group = data;
@@ -863,8 +919,8 @@ static void interface_capability_pairwise(DBusMessageIter 
*iter,
                        break;
                }
 }
-
 static void interface_capability_group(DBusMessageIter *iter, void *user_data)
+
 {
        GSupplicantInterface *interface = user_data;
        const char *str = NULL;
@@ -2209,6 +2265,15 @@ static void interface_bss_removed(DBusMessageIter *iter, 
void *user_data)
                g_hash_table_remove(interface->network_table, network->group);
 }
 
+static void wps_process_credentials(DBusMessageIter *iter, void *user_data)
+{
+       dbus_bool_t credentials = FALSE;
+
+       SUPPLICANT_DBG("");
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &credentials);
+}
+
 static void set_config_methods(DBusMessageIter *iter, void *user_data)
 {
        dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, user_data);
@@ -2285,6 +2350,15 @@ static void interface_property(const char *key, 
DBusMessageIter *iter,
                                                                interface);
                if (interface->mode_capa & G_SUPPLICANT_CAPABILITY_MODE_P2P)
                        interface->p2p_support = true;
+
+               if (interface->keymgmt_capa & G_SUPPLICANT_KEYMGMT_WPS) {
+                       interface->wps_support = true;
+
+                       supplicant_dbus_property_set(interface->path,
+                               SUPPLICANT_INTERFACE ".Interface.WPS",
+                               "ProcessCredentials", 
DBUS_TYPE_BOOLEAN_AS_STRING,
+                               wps_process_credentials, NULL, NULL, NULL);
+               }
        } else if (g_strcmp0(key, "State") == 0) {
                const char *str = NULL;
 
@@ -2846,13 +2920,50 @@ static void signal_bss_changed(const char *path, 
DBusMessageIter *iter)
        callback_network_changed(network, "Signal");
 }
 
+static void wps_credentials_encrtype(DBusMessageIter *iter, void *user_data)
+{
+       unsigned int *encrtype = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (!str)
+               return;
+
+       for (i = 0; wps_encr_map[i].str; i++) {
+               if (strcmp(str, wps_encr_map[i].str) == 0) {
+                       *encrtype |= wps_encr_map[i].val;
+                       break;
+               }
+       }
+}
+
+static void wps_credentials_authtype(DBusMessageIter *iter, void *user_data)
+{
+       unsigned int *authtype = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (!str)
+               return;
+
+       for (i = 0; wps_auth_map[i].str; i++) {
+               if (strcmp(str, wps_auth_map[i].str) == 0) {
+                       *authtype |= wps_auth_map[i].val;
+                       break;
+               }
+       }
+}
+
 static void wps_credentials(const char *key, DBusMessageIter *iter,
                        void *user_data)
 {
        GSupplicantInterface *interface = user_data;
 
-       if (!key)
+       if (!key) {
                return;
+       }
 
        SUPPLICANT_DBG("key %s", key);
 
@@ -2890,12 +3001,27 @@ static void wps_credentials(const char *key, 
DBusMessageIter *iter,
                        memset(interface->wps_cred.ssid, 0, 32);
                        interface->wps_cred.ssid_len = 0;
                }
+               SUPPLICANT_DBG("WPS SSID present : %s", 
interface->wps_cred.ssid);
+       } else if (g_strcmp0(key, "AuthType") == 0) {
+               DBusMessageIter array;
+               dbus_message_iter_recurse(iter, &array);
+               supplicant_dbus_array_foreach(iter,
+                                         wps_credentials_authtype, 
&interface->wps_cred.authtype);
+               SUPPLICANT_DBG("WPS AuthType=0x%04x", 
interface->wps_cred.authtype);
+       } else if (g_strcmp0(key, "EncrType") == 0) {
+               DBusMessageIter array;
+               dbus_message_iter_recurse(iter, &array);
+               supplicant_dbus_array_foreach(iter,
+                                         wps_credentials_encrtype, 
&interface->wps_cred.encrtype);
+               SUPPLICANT_DBG("WPS EncrType=0x%04x", 
interface->wps_cred.encrtype);
        }
 }
 
 static void signal_wps_credentials(const char *path, DBusMessageIter *iter)
 {
        GSupplicantInterface *interface;
+       struct g_supplicant_bss bss;
+       char *group;
 
        SUPPLICANT_DBG("");
 
@@ -2904,23 +3030,62 @@ static void signal_wps_credentials(const char *path, 
DBusMessageIter *iter)
                return;
 
        supplicant_dbus_property_foreach(iter, wps_credentials, interface);
+
+       memset(&bss, 0, sizeof(struct g_supplicant_bss));
+       memcpy(bss.ssid, interface->wps_cred.ssid, 
interface->wps_cred.ssid_len);
+       bss.ssid_len = interface->wps_cred.ssid_len;
+       bss.mode = G_SUPPLICANT_MODE_INFRA;
+       bss.security = G_SUPPLICANT_SECURITY_UNKNOWN;
+       if (interface->wps_cred.encrtype &
+                       (G_SUPPLICANT_WPS_ENCR_AES | 
G_SUPPLICANT_WPS_ENCR_TKIP)) {
+               bss.security = G_SUPPLICANT_SECURITY_PSK;
+       }
+       else if (interface->wps_cred.encrtype & G_SUPPLICANT_WPS_ENCR_WEP) {
+               bss.security = G_SUPPLICANT_SECURITY_WEP;
+       }
+       else if (interface->wps_cred.encrtype & G_SUPPLICANT_WPS_ENCR_NONE) {
+               bss.security = G_SUPPLICANT_SECURITY_NONE;
+       }
+
+       group = create_group(&bss);
+       callback_wps_credential(interface, group);
+       g_free(group);
 }
 
 static void wps_event_args(const char *key, DBusMessageIter *iter,
                        void *user_data)
 {
-       GSupplicantInterface *interface = user_data;
+       struct wps_event_args *args = user_data;
 
-       if (!key || !interface)
+       if (!key)
                return;
 
        SUPPLICANT_DBG("Arg Key %s", key);
+
+       if (g_strcmp0(key, "config_error") == 0) {
+               dbus_message_iter_get_basic(iter, &args->config_error);
+               SUPPLICANT_DBG("Configuration Error %d", args->config_error);
+       }
+       else if (g_strcmp0(key, "msg") == 0) {
+               dbus_message_iter_get_basic(iter, &args->msg);
+               SUPPLICANT_DBG("Msg %d", args->msg);
+       }
+       else if (g_strcmp0(key, "error_indication") == 0) {
+               dbus_message_iter_get_basic(iter, &args->error_indication);
+               SUPPLICANT_DBG("Error Indication %d", args->error_indication);
+       }
 }
 
 static void signal_wps_event(const char *path, DBusMessageIter *iter)
 {
        GSupplicantInterface *interface;
        const char *name = NULL;
+       struct wps_event_args args;
+       int error;
+
+       static const int WPS_CFG_MSG_TIMEOUT = 16;
+       static const int WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18;
+       static const int WPS_EI_SECURITY_WEP_PROHIBITED = 2;
 
        SUPPLICANT_DBG("");
 
@@ -2932,19 +3097,43 @@ static void signal_wps_event(const char *path, 
DBusMessageIter *iter)
 
        SUPPLICANT_DBG("Name: %s", name);
 
-       if (g_strcmp0(name, "success") == 0)
-               interface->wps_state = G_SUPPLICANT_WPS_STATE_SUCCESS;
-       else if (g_strcmp0(name, "fail") == 0)
-               interface->wps_state = G_SUPPLICANT_WPS_STATE_FAIL;
-       else
-               interface->wps_state = G_SUPPLICANT_WPS_STATE_UNKNOWN;
-
-       if (!dbus_message_iter_has_next(iter))
+       if (dbus_message_iter_has_next(iter)) {
+               dbus_message_iter_next(iter);
+               memset(&args, 0, sizeof(args));
+               supplicant_dbus_property_foreach(iter, wps_event_args, &args);
+       }
+       else {
                return;
+       }
 
-       dbus_message_iter_next(iter);
+       if (g_strcmp0(name, "success") == 0) {
+               interface->wps_state = G_SUPPLICANT_WPS_STATE_SUCCESS;
+               error = 0;
+       }
+       else if (g_strcmp0(name, "fail") == 0) {
+               interface->wps_state = G_SUPPLICANT_WPS_STATE_FAIL;
+               SUPPLICANT_DBG("WPS: Failed. msg = %d config_error = %d 
error_indication %d",
+                               args.msg, args.config_error, 
args.error_indication);
+
+               if (args.config_error == WPS_CFG_MSG_TIMEOUT)
+                       error = -ETIMEDOUT;
+               else if (args.config_error == WPS_CFG_DEV_PASSWORD_AUTH_FAILURE)
+                       error = -EKEYREJECTED;
+               else if (args.error_indication == 
WPS_EI_SECURITY_WEP_PROHIBITED)
+                       error = -EACCES;
+               else
+                       error = -ECONNREFUSED;
+       }
+       else if (g_strcmp0(name, "pbc-overlap") == 0) {
+               interface->wps_state = G_SUPPLICANT_WPS_STATE_FAIL;
+               error = -EAGAIN;
+       } else {
+               // M2D Events are not forwarded
+               interface->wps_state = G_SUPPLICANT_WPS_STATE_FAIL;
+               error = -ECONNREFUSED;
+       }
 
-       supplicant_dbus_property_foreach(iter, wps_event_args, interface);
+       callback_wps_event(interface, interface->wps_state, error);
 }
 
 static void create_peer_identifier(GSupplicantPeer *peer)
@@ -3661,6 +3850,14 @@ int 
g_supplicant_interface_set_country(GSupplicantInterface *interface,
        return ret;
 }
 
+bool g_supplicant_interface_support_wps(GSupplicantInterface *interface)
+{
+       if (!interface)
+               return false;
+
+       return interface->wps_support;
+}
+
 bool g_supplicant_interface_has_p2p(GSupplicantInterface *interface)
 {
        if (!interface)
@@ -4887,6 +5084,60 @@ static void interface_add_network_params(DBusMessageIter 
*iter, void *user_data)
        supplicant_dbus_dict_close(iter, &dict);
 }
 
+static void interface_wps_cancel_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       int err;
+       struct interface_connect_data *data = user_data;
+
+       SUPPLICANT_DBG("");
+
+       err = 0;
+       if (error) {
+               SUPPLICANT_DBG("WPS-Error: %s", error);
+
+               if (g_strcmp0(SUPPLICANT_INTERFACE ".InvalidArgs", error) == 0)
+                       err = -EINVAL;
+       }
+
+       if (!data) {
+               SUPPLICANT_DBG("data is NULL");
+               return;
+       }
+
+       if (data->callback)
+               data->callback(err, data->interface, data->user_data);
+
+       g_free(data->path);
+       dbus_free(data);
+}
+
+int g_supplicant_interface_wps_cancel(GSupplicantInterface *interface,
+                                       GSupplicantInterfaceCallback callback, 
void * user_data)
+{
+       struct interface_connect_data *data;
+       int ret;
+
+       data = dbus_malloc0(sizeof(*data));
+       data->interface = interface;
+       data->path = g_strdup(interface->path);
+       data->callback = callback;
+       data->user_data = user_data;
+
+       ret = supplicant_dbus_method_call(data->path,
+                       SUPPLICANT_INTERFACE ".Interface.WPS", "Cancel",
+                       NULL, interface_wps_cancel_result, data, NULL);
+
+       if (ret < 0) {
+               SUPPLICANT_DBG("WPS-Error: %d", ret);
+               g_free(data->path);
+               dbus_free(data);
+               return ret;
+       }
+
+       return -EINPROGRESS;
+}
+
 static void interface_wps_start_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
@@ -4905,29 +5156,33 @@ static void interface_wps_start_result(const char 
*error,
                data->callback(err, data->interface, data->user_data);
 
        g_free(data->path);
-       g_free(data->ssid);
+       g_free(data->wps);
        dbus_free(data);
 }
 
 static void interface_add_wps_params(DBusMessageIter *iter, void *user_data)
 {
        struct interface_connect_data *data = user_data;
-       GSupplicantSSID *ssid = data->ssid;
-       const char *role = "enrollee", *type;
+       GSupplicantWPSParams *wps = data->wps;
+       const char *role, *type;
        DBusMessageIter dict;
 
        SUPPLICANT_DBG("");
 
        supplicant_dbus_dict_open(iter, &dict);
 
+       role = "enrollee";
+       if (wps->mode == G_SUPPLICANT_MODE_MASTER)
+               role = "registrar";
+
        supplicant_dbus_dict_append_basic(&dict, "Role",
                                                DBUS_TYPE_STRING, &role);
 
        type = "pbc";
-       if (ssid->pin_wps) {
+       if (wps->pin) {
                type = "pin";
                supplicant_dbus_dict_append_basic(&dict, "Pin",
-                                       DBUS_TYPE_STRING, &ssid->pin_wps);
+                                       DBUS_TYPE_STRING, &wps->pin);
        }
 
        supplicant_dbus_dict_append_basic(&dict, "Type",
@@ -4936,36 +5191,41 @@ static void interface_add_wps_params(DBusMessageIter 
*iter, void *user_data)
        supplicant_dbus_dict_close(iter, &dict);
 }
 
-static void wps_start(const char *error, DBusMessageIter *iter, void 
*user_data)
+int g_supplicant_interface_wps_start(GSupplicantInterface *interface,
+                                       GSupplicantWPSParams *wps,
+                                       GSupplicantInterfaceCallback callback, 
void *user_data)
 {
-       struct interface_connect_data *data = user_data;
+       struct interface_connect_data *data;
+       int ret;
 
        SUPPLICANT_DBG("");
 
-       if (error) {
-               SUPPLICANT_DBG("error: %s", error);
-               g_free(data->path);
-               g_free(data->ssid);
-               dbus_free(data);
-               return;
-       }
+       data = dbus_malloc0(sizeof(*data));
+       data->interface = interface;
+       data->path = g_strdup(interface->path);
+       data->callback = callback;
+       data->wps = wps;
+       data->user_data = user_data;
 
-       supplicant_dbus_method_call(data->interface->path,
+       g_free(interface->wps_cred.key);
+       memset(&interface->wps_cred, 0, sizeof(struct 
_GSupplicantWpsCredentials));
+
+       ret = supplicant_dbus_method_call(data->interface->path,
                        SUPPLICANT_INTERFACE ".Interface.WPS", "Start",
                        interface_add_wps_params,
                        interface_wps_start_result, data, NULL);
-}
 
-static void wps_process_credentials(DBusMessageIter *iter, void *user_data)
-{
-       dbus_bool_t credentials = TRUE;
-
-       SUPPLICANT_DBG("");
+       if (ret < 0) {
+               SUPPLICANT_DBG("Error: WPS Session could not be started");
+               g_free(wps);
+               g_free(data->path);
+               dbus_free(data);
+               return ret;
+       }
 
-       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &credentials);
+       return -EINPROGRESS;
 }
 
-
 int g_supplicant_interface_connect(GSupplicantInterface *interface,
                                GSupplicantSSID *ssid,
                                GSupplicantInterfaceCallback callback,
@@ -4973,6 +5233,7 @@ int g_supplicant_interface_connect(GSupplicantInterface 
*interface,
 {
        struct interface_connect_data *data;
        struct interface_data *intf_data;
+       GSupplicantWPSParams *wps;
        int ret = 0;
 
        SUPPLICANT_DBG("");
@@ -4985,6 +5246,20 @@ int g_supplicant_interface_connect(GSupplicantInterface 
*interface,
 
        /* TODO: Check if we're already connected and switch */
 
+       if (ssid->use_wps) {
+               wps = g_try_malloc0(sizeof(GSupplicantWPSParams));
+               if (!wps)
+                       return -ENOMEM;
+
+               memset(wps, 0, sizeof(*wps));
+               wps->mode = G_SUPPLICANT_MODE_INFRA;
+               wps->pin = ssid->pin_wps;
+
+               g_free(ssid);
+               return g_supplicant_interface_wps_start(interface,
+                                                       wps, callback, 
user_data);
+       }
+
        data = dbus_malloc0(sizeof(*data));
        if (!data)
                return -ENOMEM;
@@ -4995,72 +5270,62 @@ int g_supplicant_interface_connect(GSupplicantInterface 
*interface,
        data->ssid = ssid;
        data->user_data = user_data;
 
-       if (ssid->use_wps) {
-               g_free(interface->wps_cred.key);
-               memset(&interface->wps_cred, 0,
-                               sizeof(struct _GSupplicantWpsCredentials));
+       /* By the time there is a request for connect and the network
+        * path is not NULL it means that connman has not removed the
+        * previous network pointer. This can happen in the case AP
+        * deauthenticated client and connman does not remove the
+        * previously connected network pointer. This causes supplicant
+        * to reallocate the memory for struct wpa_ssid again even if it
+        * is the same SSID. This causes memory usage of wpa_supplicnat
+        * to go high. The idea here is that if the previously connected
+        * network is not removed at the time of next connection attempt
+        * check if the network path is not NULL. In case it is non-NULL
+        * first remove the network and then once removal is successful, add
+        * the network.
+        */
 
-               ret = supplicant_dbus_property_set(interface->path,
-                       SUPPLICANT_INTERFACE ".Interface.WPS",
-                       "ProcessCredentials", DBUS_TYPE_BOOLEAN_AS_STRING,
-                       wps_process_credentials, wps_start, data, interface);
-       } else {
-               /* By the time there is a request for connect and the network
-                * path is not NULL it means that connman has not removed the
-                * previous network pointer. This can happen in the case AP
-                * deauthenticated client and connman does not remove the
-                * previously connected network pointer. This causes supplicant
-                * to reallocate the memory for struct wpa_ssid again even if it
-                * is the same SSID. This causes memory usage of wpa_supplicnat
-                * to go high. The idea here is that if the previously connected
-                * network is not removed at the time of next connection attempt
-                * check if the network path is not NULL. In case it is non-NULL
-                * first remove the network and then once removal is 
successful, add
-                * the network.
+       if (interface->network_path != NULL) {
+               g_free(data->path);
+               dbus_free(data);
+
+               /*
+                * If this add network is for the same network for
+                * which wpa_supplicant already has a profile then do
+                * not need to add another profile. Only if the
+                * profile that needs to get added is different from
+                * what is there in wpa_s delete the current one. A
+                * network is identified by its SSID, security_type
+                * and passphrase (private passphrase in case security
+                * type is 802.11x).
                 */
+               if (compare_network_parameters(interface, ssid)) {
+                       return -EALREADY;
+               }
+
+               intf_data = dbus_malloc0(sizeof(*intf_data));
+               if (!intf_data)
+                       return -ENOMEM;
+
+               intf_data->interface = interface;
+               intf_data->path = g_strdup(interface->path);
+               intf_data->callback = callback;
+               intf_data->ssid = ssid;
+               intf_data->user_data = user_data;
+               intf_data->network_remove_in_progress = TRUE;
+               network_remove(intf_data);
+       } else {
+               ret = supplicant_dbus_method_call(interface->path,
+                               SUPPLICANT_INTERFACE ".Interface", "AddNetwork",
+                               interface_add_network_params,
+                               interface_add_network_result, data,
+                               interface);
 
-               if (interface->network_path != NULL) {
+               if (ret < 0) {
                        g_free(data->path);
+                       g_free(data->ssid);
                        dbus_free(data);
-
-                       /*
-                        * If this add network is for the same network for
-                        * which wpa_supplicant already has a profile then do
-                        * not need to add another profile. Only if the
-                        * profile that needs to get added is different from
-                        * what is there in wpa_s delete the current one. A
-                        * network is identified by its SSID, security_type
-                        * and passphrase (private passphrase in case security
-                        * type is 802.11x).
-                        */
-                       if (compare_network_parameters(interface, ssid)) {
-                               return -EALREADY;
-                       }
-
-                       intf_data = dbus_malloc0(sizeof(*intf_data));
-                       if (!intf_data)
-                               return -ENOMEM;
-
-                       intf_data->interface = interface;
-                       intf_data->path = g_strdup(interface->path);
-                       intf_data->callback = callback;
-                       intf_data->ssid = ssid;
-                       intf_data->user_data = user_data;
-                       intf_data->network_remove_in_progress = TRUE;
-                       network_remove(intf_data);
-               } else {
-                       ret = supplicant_dbus_method_call(interface->path,
-                                       SUPPLICANT_INTERFACE ".Interface", 
"AddNetwork",
-                                       interface_add_network_params,
-                                       interface_add_network_result, data,
-                                       interface);
+                       return ret;
                }
-        }
-
-       if (ret < 0) {
-               g_free(data->path);
-               dbus_free(data);
-               return ret;
        }
 
        return -EINPROGRESS;
@@ -5847,3 +6112,23 @@ void g_supplicant_unregister(const GSupplicantCallbacks 
*callbacks)
        callbacks_pointer = NULL;
        eap_methods = 0;
 }
+
+static void interface_flushBSS_params(DBusMessageIter *iter, void *user_data)
+{
+       unsigned int* age = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, age);
+}
+
+void g_supplicant_interface_flushBSS(GSupplicantInterface *interface, unsigned 
int age)
+{
+       if (!interface)
+               return;
+
+       SUPPLICANT_DBG("");
+
+       supplicant_dbus_method_call(interface->path,
+               SUPPLICANT_INTERFACE ".Interface", "FlushBSS",
+               interface_flushBSS_params, NULL,
+               &age, interface);
+}
diff --git a/include/technology.h b/include/technology.h
index 97db6607..122fcb37 100644
--- a/include/technology.h
+++ b/include/technology.h
@@ -34,10 +34,20 @@ extern "C" {
  * @short_description: Functions for handling technology details
  */
 
+enum connman_technology_wps_mode {
+       CONNMAN_TECHNOLOGY_WPS_STA_MODE         = 0,
+       CONNMAN_TECHNOLOGY_WPS_AP_MODE          = 1,
+};
+
 struct connman_technology;
 
 int connman_technology_tethering_notify(struct connman_technology *technology,
                                                        bool enabled);
+
+void connman_technology_wps_state_change_notify(
+                                                       struct 
connman_technology *technology,
+                                                       const char *state);
+
 int connman_technology_set_regdom(const char *alpha2);
 void connman_technology_regdom_notify(struct connman_technology *technology,
                                                        const char *alpha2);
@@ -46,6 +56,12 @@ bool connman_technology_get_wifi_tethering(const char **ssid,
                                                        const char **psk);
 bool connman_technology_is_tethering_allowed(enum connman_service_type type);
 
+void connman_technology_add_wps_offered(struct connman_technology *technology,
+                                                                               
const char *path);
+void connman_technology_reply_start_sta_wps(
+                                                                       struct 
connman_technology *technology,
+                                                                       int 
error);
+
 struct connman_technology_driver {
        const char *name;
        enum connman_service_type type;
@@ -62,6 +78,10 @@ struct connman_technology_driver {
                                const char *bridge, bool enabled);
        int (*set_regdom) (struct connman_technology *technology,
                                                const char *alpha2);
+       int (*start_wps) (struct connman_technology *technology,
+                                               enum 
connman_technology_wps_mode mode,
+                                               const char *wps_pin);
+       int (*cancel_wps) (struct connman_technology *technology);
 };
 
 int connman_technology_driver_register(struct connman_technology_driver 
*driver);
diff --git a/plugins/wifi.c b/plugins/wifi.c
index dc08c6af..cd0cbac7 100644
--- a/plugins/wifi.c
+++ b/plugins/wifi.c
@@ -42,6 +42,7 @@
 #include <glib.h>
 
 #define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman.h>
 #include <connman/plugin.h>
 #include <connman/inet.h>
 #include <connman/device.h>
@@ -123,6 +124,12 @@ struct wifi_tethering_info {
        GSupplicantSSID *ssid;
 };
 
+struct wifi_wps_offered_data {
+       struct connman_service *service;
+       char *expect_ident;
+       char *passphrase;
+};
+
 struct wifi_data {
        char *identifier;
        struct connman_device *device;
@@ -156,11 +163,19 @@ struct wifi_data {
        unsigned int p2p_connection_timeout;
        struct connman_peer *pending_peer;
        GSList *peers;
+       bool p2p_connected;
        bool p2p_connecting;
        bool p2p_device;
        int servicing;
        int disconnect_code;
        int assoc_code;
+
+       bool wps_running;
+       bool wps_success;
+       unsigned int wps_timeout;
+       GSList *wps_offered_list;
+       bool wps_after_scan;
+       GSupplicantWPSParams wps_params;
 };
 
 static GList *iface_list = NULL;
@@ -174,6 +189,25 @@ static int tech_set_tethering(struct connman_technology 
*technology,
                                const char *identifier, const char *passphrase,
                                const char *bridge, bool enabled);
 
+/* WPS Functions */
+#define WPS_TIMEOUT_INTERVAL_MSECS (125 * 1000)
+
+static void wps_event_success(GSupplicantInterface *interface);
+static void wps_event_fail(GSupplicantInterface *interface, int error);
+static struct wifi_wps_offered_data *wps_alloc_offered_data(
+               struct wifi_data *wifi, const char *group, const char 
*passphrase);
+static void wps_free_offered_data(struct wifi_wps_offered_data *wps_data);
+static unsigned int wps_get_accepted_count(struct wifi_data *wifi);
+static unsigned int wps_get_offered_count(struct wifi_data *wifi);
+static bool wps_try_update_services(struct wifi_data *wifi);
+static void wps_finish(struct wifi_data *wifi, int error);
+static void wps_cleanup(struct wifi_data *wifi);
+static void wps_scan_callback(int result, GSupplicantInterface *interface,
+               void *user_data);
+static gboolean wps_timeout(gpointer data);
+static void wps_retry_callback(int result, GSupplicantInterface *interface,
+               void *user_data);
+
 static int p2p_tech_probe(struct connman_technology *technology)
 {
        p2p_technology = technology;
@@ -417,6 +451,29 @@ static int peer_disconnect(struct connman_peer *peer)
        return ret;
 }
 
+static int peer_disconnect_all(struct wifi_data *wifi)
+{
+       GSList *list;
+       int result = 0, err = -EALREADY;
+
+       DBG("");
+
+       for (list = wifi->peers; list; list = list->next) {
+               struct connman_peer *peer = list->data;
+               GSupplicantPeer *gs_peer =
+                                       
g_supplicant_interface_peer_lookup(wifi->interface,
+                                               
connman_peer_get_identifier(peer));
+
+               if (g_supplicant_peer_is_in_a_group(gs_peer)) {
+                       err = peer_disconnect(peer);
+                       if (err != -EINPROGRESS)
+                               result = err;
+               }
+       }
+
+       return result;
+}
+
 struct peer_service_registration {
        peer_service_registration_cb_t callback;
        void *user_data;
@@ -1583,6 +1640,7 @@ static int wifi_disable(struct connman_device *device)
                return -ENODEV;
 
        wifi->connected = false;
+       wifi->p2p_connected = false;
        wifi->disconnecting = false;
 
        if (wifi->pending_network)
@@ -2176,9 +2234,6 @@ static void ssid_init(GSupplicantSSID *ssid, struct 
connman_network *network)
                                                "WiFi.PrivateKeyPassphrase");
        ssid->phase2_auth = connman_network_get_string(network, "WiFi.Phase2");
 
-       ssid->use_wps = connman_network_get_bool(network, "WiFi.UseWPS");
-       ssid->pin_wps = connman_network_get_string(network, "WiFi.PinWPS");
-
        if (connman_setting_get_bool("BackgroundScanning"))
                ssid->bgscan = BGSCAN_DEFAULT;
 }
@@ -2199,6 +2254,9 @@ static int network_connect(struct connman_network 
*network)
        if (!wifi)
                return -ENODEV;
 
+       if (wifi->wps_running)
+               return -EBUSY;
+
        ssid = g_try_malloc0(sizeof(GSupplicantSSID));
        if (!ssid)
                return -ENOMEM;
@@ -2375,6 +2433,8 @@ static bool handle_wps_completion(GSupplicantInterface 
*interface,
 {
        bool wps;
 
+       DBG("");
+
        wps = connman_network_get_bool(network, "WiFi.UseWPS");
        if (wps) {
                const unsigned char *ssid, *wps_ssid;
@@ -2486,9 +2546,40 @@ static void interface_state(GSupplicantInterface 
*interface)
                finalize_interface_creation(wifi);
        }
 
+       if (wifi->wps_running) {
+               DBG("WPS running");
+               switch (state) {
+               case G_SUPPLICANT_STATE_SCANNING:
+                       if (!wifi->wps_success) {
+                               
connman_technology_wps_state_change_notify(wifi_technology, "scan");
+                       }
+                       return;
+               case G_SUPPLICANT_STATE_ASSOCIATED:
+                       
connman_technology_wps_state_change_notify(wifi_technology,
+                                                                               
                           "processing");
+                       return;
+               case G_SUPPLICANT_STATE_DISCONNECTED:
+                       if (wifi->wps_success) {
+                               if (wps_try_update_services(wifi)) {
+                                       wps_finish(wifi, 0);
+                               }
+                               else {
+                                       wifi->wps_after_scan = true;
+                                       DBG("Need scan");
+                                       reset_autoscan(wifi->device);
+                                       throw_wifi_scan(device, 
wps_scan_callback);
+                               }
+                       }
+                       return;
+               default:
+                       return;
+               }
+       }
+
        network = wifi->network;
-       if (!network)
+       if (!network) {
                return;
+       }
 
        switch (state) {
        case G_SUPPLICANT_STATE_SCANNING:
@@ -2510,9 +2601,6 @@ static void interface_state(GSupplicantInterface 
*interface)
                /* though it should be already stopped: */
                stop_autoscan(device);
 
-               if (!handle_wps_completion(interface, network, device, wifi))
-                       break;
-
                connman_network_set_connected(network, true);
 
                wifi->disconnect_code = 0;
@@ -2760,9 +2848,6 @@ static void network_added(GSupplicantNetwork 
*supplicant_network)
        const unsigned char *ssid;
        unsigned int ssid_len;
        bool wps;
-       bool wps_pbc;
-       bool wps_ready;
-       bool wps_advertizing;
 
        mode = g_supplicant_network_get_mode(supplicant_network);
        identifier = g_supplicant_network_get_identifier(supplicant_network);
@@ -2778,10 +2863,6 @@ static void network_added(GSupplicantNetwork 
*supplicant_network)
        security = g_supplicant_network_get_security(supplicant_network);
        group = g_supplicant_network_get_identifier(supplicant_network);
        wps = g_supplicant_network_get_wps(supplicant_network);
-       wps_pbc = g_supplicant_network_is_wps_pbc(supplicant_network);
-       wps_ready = g_supplicant_network_is_wps_active(supplicant_network);
-       wps_advertizing = g_supplicant_network_is_wps_advertizing(
-                                                       supplicant_network);
 
        if (!wifi)
                return;
@@ -2815,16 +2896,6 @@ static void network_added(GSupplicantNetwork 
*supplicant_network)
        connman_network_set_strength(network,
                                calculate_strength(supplicant_network));
        connman_network_set_bool(network, "WiFi.WPS", wps);
-       connman_network_set_bool(network, "WiFi.WPSAdvertising",
-                               wps_advertizing);
-
-       if (wps) {
-               /* Is AP advertizing for WPS association?
-                * If so, we decide to use WPS by default */
-               if (wps_ready && wps_pbc &&
-                                               wps_advertizing)
-                       connman_network_set_bool(network, "WiFi.UseWPS", true);
-       }
 
        connman_network_set_frequency(network,
                        g_supplicant_network_get_frequency(supplicant_network));
@@ -3009,6 +3080,33 @@ static void apply_peer_services(GSupplicantPeer *peer,
        }
 }
 
+static void wps_event(GSupplicantInterface *interface,
+                                                       GSupplicantWpsState 
state, int error)
+{
+       struct wifi_data *wifi = g_supplicant_interface_get_data(interface);
+
+       /* Do nothing here for P2P Connections */
+       if (wifi->p2p_connecting)
+               return;
+
+       if (!wifi->wps_running)
+               return;
+
+       switch (state) {
+               case G_SUPPLICANT_WPS_STATE_SUCCESS:
+                       DBG("WPS-STA-Event: Success");
+                       wps_event_success(interface);
+                       break;
+               case G_SUPPLICANT_WPS_STATE_FAIL:
+                       DBG("WPS-STA-Event: Fail %d", error);
+                       wps_event_fail(interface, error);
+                       break;
+               default:
+                       DBG("WPS-STA-Event: Not defined");
+                       return;
+       }
+}
+
 static void peer_found(GSupplicantPeer *peer)
 {
        GSupplicantInterface *iface = g_supplicant_peer_get_interface(peer);
@@ -3115,6 +3213,10 @@ static void peer_changed(GSupplicantPeer *peer, 
GSupplicantPeerState state)
                break;
        }
 
+       if (p_state == CONNMAN_PEER_STATE_FAILURE ||
+                                       p_state == CONNMAN_PEER_STATE_IDLE)
+               wifi->p2p_connected = false;
+
        if (p_state == CONNMAN_PEER_STATE_CONFIGURATION ||
                                        p_state == CONNMAN_PEER_STATE_FAILURE) {
                if (wifi->p2p_connecting
@@ -3131,6 +3233,8 @@ static void peer_changed(GSupplicantPeer *peer, 
GSupplicantPeerState state)
                GSupplicantInterface *g_iface;
                struct wifi_data *g_wifi;
 
+               wifi->p2p_connected = true;
+
                g_iface = g_supplicant_peer_get_group_interface(peer);
                if (!g_iface)
                        return;
@@ -3198,6 +3302,23 @@ static void assoc_status_code(GSupplicantInterface 
*interface, int status_code)
        }
 }
 
+static void wps_credential(GSupplicantInterface *interface, const char *group)
+{
+       struct wifi_data *wifi = g_supplicant_interface_get_data(interface);
+       struct wifi_wps_offered_data *wps_data;
+
+       DBG("");
+
+       wps_data = wps_alloc_offered_data(wifi, group,
+               g_supplicant_interface_get_wps_key(interface));
+       DBG("%s", wps_data->expect_ident);
+
+       connman_technology_wps_state_change_notify(wifi_technology, 
"cred-received");
+
+       wifi->wps_offered_list = g_slist_append(wifi->wps_offered_list,
+                       wps_data);
+}
+
 static const GSupplicantCallbacks callbacks = {
        .system_ready           = system_ready,
        .system_killed          = system_killed,
@@ -3212,6 +3333,7 @@ static const GSupplicantCallbacks callbacks = {
        .network_removed        = network_removed,
        .network_changed        = network_changed,
        .network_associated     = network_associated,
+       .wps_event              = wps_event,
        .peer_found             = peer_found,
        .peer_lost              = peer_lost,
        .peer_changed           = peer_changed,
@@ -3219,6 +3341,7 @@ static const GSupplicantCallbacks callbacks = {
        .debug                  = debug,
        .disconnect_reasoncode  = disconnect_reasoncode,
        .assoc_status_code      = assoc_status_code,
+       .wps_credential = wps_credential
 };
 
 
@@ -3517,6 +3640,499 @@ static int tech_set_regdom(struct connman_technology 
*technology, const char *al
        return g_supplicant_set_country(alpha2, regdom_callback, NULL);
 }
 
+static void start_wps_callback(int result, GSupplicantInterface *interface,
+                                                       void *user_data)
+{
+       GSupplicantWPSParams *wps = user_data;
+       struct wifi_data *wifi = g_supplicant_interface_get_data(interface);
+
+       DBG("");
+
+       if (result) {
+               DBG("Error while starting WPS Session %d", result);
+
+               if (wps->mode == G_SUPPLICANT_MODE_INFRA) {
+                       connman_technology_reply_start_sta_wps(wifi_technology, 
-EINVAL);
+               }
+               return;
+       }
+
+       wps_cleanup(wifi);
+       wifi->wps_running = true;
+       wifi->wps_timeout = g_timeout_add(WPS_TIMEOUT_INTERVAL_MSECS,
+                       wps_timeout, interface);
+       memset(&wifi->wps_params, 0, sizeof(wifi->wps_params));
+       wifi->wps_params.pin = wps->pin;
+       wifi->wps_params.mode = wps->mode;
+       stop_autoscan(wifi->device);
+}
+
+/*
+ * Start WPS Session in STA mode.
+ *
+ * @skip_ap: When it is TRUE then this function will not try to start WPS on
+ * the interfaces that support AP mode. Those will be skipped.
+ * @skip_p2p: When it is TRUE then this function will not try to start WPS on
+ * the interfaces that support P2P mode. Those will be skipped.
+ *
+ * It is always assumed that P2P support always implies AP support
+ */
+static int start_sta_wps(const char *wps_pin, bool skip_ap, bool skip_p2p)
+{
+       GList *list;
+       GSupplicantInterface *interface;
+       GSupplicantWPSParams *wps;
+       struct wifi_data *wifi;
+
+       const char *ifname;
+       bool p2p_support, ap_support, sta_support, wps_support;
+       unsigned int mode;
+       int err;
+
+
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
+
+               interface = wifi->interface;
+               ifname = g_supplicant_interface_get_ifname(wifi->interface);
+               mode = g_supplicant_interface_get_mode(interface);
+
+               wps_support = g_supplicant_interface_support_wps(interface);
+               p2p_support = g_supplicant_interface_has_p2p(interface);
+               sta_support = mode & G_SUPPLICANT_CAPABILITY_MODE_INFRA ? true 
: false;
+               ap_support = !(wifi->ap_supported == WIFI_AP_NOT_SUPPORTED) &&
+                                               (mode & 
G_SUPPLICANT_CAPABILITY_MODE_AP) ? true : false;
+
+               if (!wps_support || !sta_support) {
+                       DBG("Skipping %s. Required capabilities are missing", 
ifname);
+                       continue;
+               }
+
+               if (p2p_support && skip_p2p) {
+                       DBG("Skipping %s. It also supports P2P/AP modes", 
ifname);
+                       continue;
+               }
+
+               if ((ap_support && !p2p_support) && skip_ap) {
+                       DBG("Skipping %s. It also supports AP mode", ifname);
+                       continue;
+               }
+
+               if (wifi->tethering) {
+                       DBG("Skipping %s. It is busy (Tethering)", ifname);
+                       continue;
+               }
+
+               if (wifi->connected && wifi->network) {
+                       err = network_disconnect(wifi->network);
+                       if (err < 0)
+                               return err;
+               }
+
+               if (wifi->p2p_connected) {
+                       err = peer_disconnect_all(wifi);
+                       if (err < 0)
+                               return err;
+               }
+
+               wps = g_try_malloc0(sizeof(GSupplicantWPSParams));
+               if (!wps)
+                       return -ENOMEM;
+
+               memset(wps, 0, sizeof(*wps));
+               wps->pin = wps_pin;
+               wps->mode = G_SUPPLICANT_MODE_INFRA;
+
+               DBG("Trying to start WPS on %s", ifname);
+               return g_supplicant_interface_wps_start(interface, wps,
+                                       start_wps_callback, wps);
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int sta_wps(const char *wps_pin)
+{
+       int err;
+
+       /* It is preferred to use the STA-only interfaces */
+       DBG("Try on STA-only interfaces");
+       err = start_sta_wps(wps_pin, true, true);
+       if (err == -EOPNOTSUPP) {
+               /*
+                * Any STA-only interfaces available, so let's try on STA/AP
+                * interfaces. P2P interface will be only considered in the 
case it
+                * is the last option.
+                */
+               DBG("Try on STA/AP interfaces");
+
+               err = start_sta_wps(wps_pin, false, true);
+               if (err == -EOPNOTSUPP) {
+                       /*
+                        * Neither STA/AP interfaces are available thus let's 
try on
+                        * STA/AP/P2P interfaces.
+                        */
+                       DBG("Try on STA/AP/P2P interfaces");
+
+                       return start_sta_wps(wps_pin, true, false);
+               }
+       }
+
+       return err;
+}
+
+static int tech_start_wps(struct connman_technology *technology,
+                                                               enum 
connman_technology_wps_mode mode,
+                                                               const char 
*wps_pin)
+{
+       GList *list;
+       GSupplicantInterface *interface;
+       struct wifi_data *wifi;
+       GSupplicantWPSParams *wps;
+
+       DBG("");
+
+       if (mode == CONNMAN_TECHNOLOGY_WPS_STA_MODE)
+               return sta_wps(wps_pin);
+
+       /*
+        * For WPS in AP mode, we just need to look for the interface on which
+        * tethering is enabled and start WPS on it.
+        */
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
+
+               if (!wifi->tethering)
+                       continue;
+
+               interface = wifi->interface;
+
+               if (!g_supplicant_interface_support_wps(interface)) {
+                       DBG("Error: WPS is not supported on tethering 
interface");
+                       return -EOPNOTSUPP;
+               }
+
+               wps = g_try_malloc0(sizeof(GSupplicantWPSParams));
+               if (!wps)
+                       return -ENOMEM;
+
+               memset(wps, 0, sizeof(*wps));
+               wps->pin = wps_pin;
+               wps->mode = G_SUPPLICANT_MODE_MASTER;
+
+               return g_supplicant_interface_wps_start(interface, wps,
+                                       start_wps_callback, wps);
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static void cancel_wps_callback(int result, GSupplicantInterface *interface,
+                                                       void *user_data)
+{
+       struct wifi_data *wifi = g_supplicant_interface_get_data(interface);
+
+       if (result < 0)
+               DBG("Error canceling WPS session on %s: %d",
+                                       
g_supplicant_interface_get_ifname(interface), result);
+
+       if (wifi) {
+               wps_cleanup(wifi);
+       }
+}
+
+static int tech_cancel_wps(struct connman_technology *technology)
+{
+       GList *list;
+       struct wifi_data *wifi;
+       GSupplicantInterface *interface;
+       int err = -EOPNOTSUPP;
+
+       DBG("");
+
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
+
+               interface = wifi->interface;
+
+               if (!g_supplicant_interface_support_wps(interface))
+                       continue;
+
+               /*
+                * Do not perform WPS Cancel on an interface already connected 
to a
+                * service or p2p device because it will result in an undesired
+                * disconnection.
+                */
+               if (wifi->connected || wifi->p2p_connected)
+                       continue;
+
+               err = g_supplicant_interface_wps_cancel(interface,
+                                               cancel_wps_callback, NULL);
+               if (err != -EINPROGRESS)
+                       break;
+       }
+
+       return err;
+}
+
+static void wps_event_success(GSupplicantInterface *interface)
+{
+       struct wifi_data *wifi = g_supplicant_interface_get_data(interface);
+
+       wifi->wps_success = true;
+}
+
+static void wps_event_fail(GSupplicantInterface *interface, int error)
+{
+       struct wifi_data *wifi = g_supplicant_interface_get_data(interface);
+       GSupplicantWPSParams *wps;
+
+       switch(error) {
+               case -ETIMEDOUT:
+               case -EKEYREJECTED:
+               case -EAGAIN:
+                       wps_finish(wifi, error);
+                       break;
+               case -EACCES:
+                       wps_finish(wifi, -ECONNREFUSED);
+                       break;
+               default:
+                       DBG("Restart WPS");
+                       wps = g_try_malloc0(sizeof(*wps));
+                       if (!wps) {
+                               DBG("No memory");
+                               wps_finish(wifi, -ENOMEM);
+                               return;
+                       }
+                       *wps = wifi->wps_params;
+                       g_supplicant_interface_wps_start(interface, wps, 
wps_retry_callback, wps);
+                       break;
+       }
+}
+
+static struct wifi_wps_offered_data *wps_alloc_offered_data(
+               struct wifi_data *wifi, const char *group, const char 
*passphrase)
+{
+       struct wifi_wps_offered_data *wps_data;
+
+       wps_data = g_try_malloc0(sizeof(*wps_data));
+       if (!wps_data) {
+               return NULL;
+       }
+
+       wps_data->expect_ident = g_strdup_printf("%s_%s_%s",
+                       network_driver.name, 
connman_device_get_ident(wifi->device), group);
+       if (!wps_data->expect_ident) {
+               goto fail;
+       }
+
+       if (!passphrase) {
+               return wps_data;
+       }
+
+       if (strlen(passphrase)) {
+               wps_data->passphrase = g_strdup(passphrase);
+               if (!wps_data->passphrase) {
+                       goto fail;
+               }
+       }
+
+       return wps_data;
+
+fail:
+       DBG("No memory");
+       g_free(wps_data->expect_ident);
+       g_free(wps_data->passphrase);
+       g_free(wps_data);
+       return NULL;
+}
+
+static void wps_free_offered_data(struct wifi_wps_offered_data *wps_data)
+{
+       if (wps_data->service) {
+               connman_service_unref(wps_data->service);
+       }
+       g_free(wps_data->expect_ident);
+       g_free(wps_data->passphrase);
+       g_free(wps_data);
+}
+
+static unsigned int wps_get_accepted_count(struct wifi_data *wifi)
+{
+       unsigned int n = 0;
+       GSList *l;
+       for (l = wifi->wps_offered_list; l; l = l->next) {
+               struct wifi_wps_offered_data *data = l->data;
+               if (data->service) {
+                       ++n;
+               }
+       }
+
+       return n;
+}
+
+static unsigned int wps_get_offered_count(struct wifi_data *wifi)
+{
+       return g_slist_length(wifi->wps_offered_list);
+}
+
+static bool wps_try_update_services(struct wifi_data *wifi)
+{
+       GSList *l;
+
+       DBG("");
+
+       for (l = wifi->wps_offered_list; l; l = l->next) {
+               struct connman_service *service;
+               struct wifi_wps_offered_data *data = l->data;
+               if (data->service) {
+                       continue;
+               }
+               service = 
__connman_service_lookup_from_ident(data->expect_ident);
+               if (!service) {
+                       continue;
+               }
+               connman_service_ref(service);
+               data->service = service;
+               if (data->passphrase) {
+                       __connman_service_set_passphrase(service, 
data->passphrase);
+               }
+               __connman_service_force_save(service);
+               DBG("service %p updated", service);
+       }
+
+       return (wps_get_offered_count(wifi) == wps_get_accepted_count(wifi));
+}
+
+static void wps_finish(struct wifi_data *wifi, int error)
+{
+       GSList* list;
+
+       DBG("wifi %p error %d", wifi, error);
+
+       if (!error) {
+               connman_technology_wps_state_change_notify(wifi_technology, 
"complete");
+               for (list = wifi->wps_offered_list; list; list = list->next) {
+                       struct wifi_wps_offered_data *data = list->data;
+                       if (!data->service) {
+                               continue;
+                       }
+                       connman_technology_add_wps_offered(wifi_technology, 
data->expect_ident);
+               }
+               connman_technology_reply_start_sta_wps(wifi_technology, 0);
+       }
+       else {
+               connman_technology_wps_state_change_notify(wifi_technology, 
"failure");
+               connman_technology_reply_start_sta_wps(wifi_technology, error);
+       }
+
+       wps_cleanup(wifi);
+       if (error) {
+               g_supplicant_interface_flushBSS(wifi->interface, 0);
+       }
+       reset_autoscan(wifi->device);
+       start_autoscan(wifi->device);
+}
+
+static void wps_cleanup(struct wifi_data *wifi)
+{
+       GSList* list;
+
+       DBG("");
+
+       for (list = wifi->wps_offered_list; list; list = list->next) {
+               wps_free_offered_data(list->data);
+       }
+       g_slist_free(wifi->wps_offered_list);
+       wifi->wps_offered_list = NULL;
+       wifi->wps_running = false;
+       wifi->wps_success = false;
+       wifi->wps_after_scan = false;
+
+       if (wifi->wps_timeout) {
+               g_source_remove(wifi->wps_timeout);
+               wifi->wps_timeout = 0;
+       }
+
+       memset(&wifi->wps_params, 0, sizeof(wifi->wps_params));
+}
+
+static void wps_scan_callback(int result, GSupplicantInterface *interface,
+               void *user_data)
+{
+       struct connman_device *device = user_data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+       bool all_service_accepted;
+
+       DBG("");
+
+       if ((!wifi->wps_running) || (!wifi->wps_after_scan)) {
+               DBG("Operation timed out or canceled");
+               connman_device_unref(device);
+               return;
+       }
+
+       all_service_accepted = wps_try_update_services(wifi);
+       if (all_service_accepted || wps_get_accepted_count(wifi)) {
+               connman_device_unref(device);
+               wps_finish(wifi, 0);
+       }
+       else {
+               int ret;
+               DBG("Retry scan");
+               ret = g_supplicant_interface_scan(wifi->interface, NULL,
+                       wps_scan_callback, device);
+               if (ret) {
+                       DBG("Scan failed");
+                       connman_device_unref(device);
+                       wps_finish(wifi, -ECONNREFUSED);
+               }
+       }
+}
+
+static gboolean wps_timeout(gpointer data)
+{
+       GSupplicantInterface *interface = data;
+       struct wifi_data *wifi;
+
+       DBG("data %p", data);
+
+       if (!interface) {
+               return FALSE;
+       }
+
+       wifi = g_supplicant_interface_get_data(interface);
+
+       if (wifi->wps_after_scan) {
+               if (wps_get_accepted_count(wifi)) {
+                       wps_finish(wifi, 0);
+                       return FALSE;
+               }
+       }
+
+       wps_finish(wifi, -ETIMEDOUT);
+       g_supplicant_interface_wps_cancel(interface, NULL, NULL);
+       return FALSE;
+}
+
+static void wps_retry_callback(int result, GSupplicantInterface *interface,
+               void *user_data)
+{
+       GSupplicantWPSParams *wps = user_data;
+       struct wifi_data *wifi = g_supplicant_interface_get_data(interface);
+
+       DBG("");
+
+       if (result) {
+               DBG("Error while restarting WPS Session %d", result);
+
+               if (wps->mode == G_SUPPLICANT_MODE_INFRA) {
+                       wps_finish(wifi, -EINVAL);
+               }
+               return;
+       }
+}
+
 static struct connman_technology_driver tech_driver = {
        .name           = "wifi",
        .type           = CONNMAN_SERVICE_TYPE_WIFI,
@@ -3524,6 +4140,8 @@ static struct connman_technology_driver tech_driver = {
        .remove         = tech_remove,
        .set_tethering  = tech_set_tethering,
        .set_regdom     = tech_set_regdom,
+       .start_wps      = tech_start_wps,
+       .cancel_wps     = tech_cancel_wps,
 };
 
 static int wifi_init(void)
diff --git a/src/connman.h b/src/connman.h
index 82e77d37..9b11df4e 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -54,6 +54,8 @@ DBusMessage *__connman_error_operation_aborted(DBusMessage 
*msg);
 DBusMessage *__connman_error_operation_timeout(DBusMessage *msg);
 DBusMessage *__connman_error_invalid_service(DBusMessage *msg);
 DBusMessage *__connman_error_invalid_property(DBusMessage *msg);
+DBusMessage *__connman_error_pin_rejected(DBusMessage *msg);
+DBusMessage *__connman_error_pbc_overlap(DBusMessage *msg);
 
 int __connman_manager_init(void);
 void __connman_manager_cleanup(void);
@@ -665,6 +667,8 @@ int __connman_service_init(void);
 void __connman_service_cleanup(void);
 int __connman_service_load_modifiable(struct connman_service *service);
 
+void __connman_service_append_struct(struct connman_service *service,
+                                                                               
         DBusMessageIter *iter);
 void __connman_service_list_struct(DBusMessageIter *iter);
 
 int __connman_service_compare(const struct connman_service *a,
@@ -843,6 +847,7 @@ int __connman_peer_service_unregister(const char *owner,
                                        int specification_length,
                                        const unsigned char *query,
                                        int query_length, int version);
+enum connman_peer_wps_method __connman_check_wps_method(const char *wpspin);
 
 #include <connman/session.h>
 
@@ -853,6 +858,7 @@ int __connman_service_iterate_services(service_iterate_cb 
cb, void *user_data);
 
 void __connman_service_mark_dirty();
 void __connman_service_save(struct connman_service *service);
+void __connman_service_force_save(struct connman_service *service);
 
 #include <connman/notifier.h>
 
diff --git a/src/error.c b/src/error.c
index 4f24ae25..f0b35bdc 100644
--- a/src/error.c
+++ b/src/error.c
@@ -67,6 +67,10 @@ DBusMessage *__connman_error_failed(DBusMessage *msg, int 
errnum)
                return __connman_error_in_progress(msg);
        case ENOKEY:
                return __connman_error_passphrase_required(msg);
+       case EKEYREJECTED:
+               return __connman_error_pin_rejected(msg);
+       case EAGAIN:
+               return __connman_error_pbc_overlap(msg);
        }
 
        return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
@@ -185,3 +189,15 @@ DBusMessage *__connman_error_invalid_property(DBusMessage 
*msg)
        return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
                                ".InvalidProperty", "Invalid property");
 }
+
+DBusMessage *__connman_error_pin_rejected(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".PinRejected", "PIN Rejected");
+}
+
+DBusMessage *__connman_error_pbc_overlap(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".PbcOverlap", "PBC Overlap");
+}
diff --git a/src/peer.c b/src/peer.c
index 1b9b80e3..70ffeab4 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -545,8 +545,7 @@ static const char *get_dbus_sender(struct connman_peer 
*peer)
        return dbus_message_get_sender(peer->pending);
 }
 
-static enum connman_peer_wps_method check_wpspin(struct connman_peer *peer,
-                                                       const char *wpspin)
+enum connman_peer_wps_method __connman_check_wps_method(const char *wpspin)
 {
        int len, i;
 
@@ -591,7 +590,7 @@ static void request_authorization_cb(struct connman_peer 
*peer,
                goto out;
        }
 
-       wps_method = check_wpspin(peer, wpspin);
+       wps_method = __connman_check_wps_method(wpspin);
 
        err = peer_driver->connect(peer, wps_method, wpspin);
        if (err == -EINPROGRESS)
diff --git a/src/service.c b/src/service.c
index 733c0728..e9ec7e2b 100644
--- a/src/service.c
+++ b/src/service.c
@@ -817,6 +817,15 @@ void __connman_service_save(struct connman_service 
*service)
        service_save(service);
 }
 
+void __connman_service_force_save(struct connman_service *service)
+{
+       if (!service)
+               return;
+
+       service->new_service = false;
+       service_save(service);
+}
+
 static enum connman_service_state combine_state(
                                        enum connman_service_state state_a,
                                        enum connman_service_state state_b)
@@ -2577,6 +2586,18 @@ static void append_struct(gpointer value, gpointer 
user_data)
        append_struct_service(iter, append_dict_properties, service);
 }
 
+void __connman_service_append_struct(struct connman_service *service,
+                                                                        
DBusMessageIter *iter)
+{
+       if (!service)
+               return;
+
+       if (!iter)
+               return;
+
+       append_struct(service, iter);
+}
+
 void __connman_service_list_struct(DBusMessageIter *iter)
 {
        g_list_foreach(service_list, append_struct, iter);
diff --git a/src/technology.c b/src/technology.c
index 4c1cbbbb..c1560cd1 100644
--- a/src/technology.c
+++ b/src/technology.c
@@ -74,6 +74,14 @@ struct connman_technology {
        DBusMessage *pending_reply;
        guint pending_timeout;
 
+       /*
+        * Used to handle WPS errors within the two-minute interval.
+        * It is done only for WPS in STA mode, because for AP the
+        * wpa_supplicant does not report any events/errors.
+        */
+       DBusMessage *wps_reply;
+       GSList *wps_offered;
+
        GSList *scan_pending;
 
        bool rfkill_driven;
@@ -277,6 +285,15 @@ static int set_tethering(struct connman_technology 
*technology,
        return result;
 }
 
+void connman_technology_wps_state_change_notify(
+                                                                       struct 
connman_technology *technology,
+                                                                       const 
char *state)
+{
+       connman_dbus_property_changed_basic(technology->path,
+                               CONNMAN_TECHNOLOGY_INTERFACE, "WpsState",
+                                               DBUS_TYPE_STRING, &state);
+}
+
 void connman_technology_regdom_notify(struct connman_technology *technology,
                                                        const char *alpha2)
 {
@@ -576,6 +593,205 @@ static void technology_removed_signal(struct 
connman_technology *technology)
                        DBUS_TYPE_INVALID);
 }
 
+void connman_technology_add_wps_offered(struct connman_technology *technology,
+                                                                               
const char *path)
+{
+       char *dup_path = g_strdup(path);
+       if (!dup_path)
+               return;
+       technology->wps_offered = g_slist_append(technology->wps_offered, 
dup_path);
+}
+
+static void append_wps_service_structs(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_technology *technology = user_data;
+       GSList *list;
+
+       for (list = technology->wps_offered; list; list = list->next) {
+               const char *ident = list->data;
+               struct connman_service *service;
+               service = __connman_service_lookup_from_ident(ident);
+               if (!service)
+                       continue;
+               __connman_service_append_struct(service, iter);
+       }
+}
+
+static DBusMessage *create_reply_start_sta_wps_success(
+                                                       struct 
connman_technology *technology,
+                                                       DBusMessage *reply)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_return(reply);
+       if (!msg)
+               return NULL;
+
+       __connman_dbus_append_objpath_dict_array(msg, 
append_wps_service_structs,
+                                                                               
         technology);
+
+       return msg;
+}
+
+static void free_wps_offered(gpointer data, gpointer user_data)
+{
+       if (!data)
+               return;
+
+       g_free(data);
+}
+
+void connman_technology_reply_start_sta_wps(struct connman_technology 
*technology,
+                                                       int error)
+{
+       DBusMessage *reply;
+
+       if (!technology->wps_reply)
+               return;
+
+       if (error < 0)
+               reply = __connman_error_failed(technology->wps_reply, -error);
+       else {
+               reply = create_reply_start_sta_wps_success(technology,
+                                                                               
                   technology->wps_reply);
+       }
+
+       g_dbus_send_message(connection, reply);
+
+       dbus_message_unref(technology->wps_reply);
+       technology->wps_reply = NULL;
+
+       g_slist_foreach(technology->wps_offered, free_wps_offered, NULL);
+       g_slist_free(technology->wps_offered);
+       technology->wps_offered = NULL;
+}
+
+static int start_wps(struct connman_technology *technology, DBusMessage *msg,
+                                                       enum 
connman_technology_wps_mode mode)
+{
+       GSList *tech_drivers;
+       DBusMessageIter iter;
+       enum connman_peer_wps_method wps_method;
+       const char *auth;
+       int err, result = -EOPNOTSUPP;
+
+       if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
+               return -EOPNOTSUPP;
+
+       __sync_synchronize();
+       if (!technology->enabled)
+               return -EACCES;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return -EINVAL;
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return -EINVAL;
+
+       dbus_message_iter_get_basic(&iter, &auth);
+
+       wps_method = __connman_check_wps_method(auth);
+       if (wps_method == CONNMAN_PEER_WPS_UNKNOWN)
+               return -EINVAL;
+       if (wps_method == CONNMAN_PEER_WPS_PBC)
+               auth = NULL;
+
+       for (tech_drivers = technology->driver_list; tech_drivers;
+                tech_drivers = g_slist_next(tech_drivers)) {
+               struct connman_technology_driver *driver = tech_drivers->data;
+
+               if (!driver || !driver->start_wps ||
+                                       driver->type != 
CONNMAN_SERVICE_TYPE_WIFI)
+                       continue;
+
+               err = driver->start_wps(technology, mode, auth);
+
+               if (result == -EINPROGRESS)
+                       continue;
+
+               if (err == -EINPROGRESS)
+                       result = err;
+       }
+
+       return result;
+}
+
+static DBusMessage *start_ap_wps(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct connman_technology *technology = user_data;
+       int err;
+
+       /* It is required to enable tethering before starting WPS in AP mode */
+       if (!technology->tethering) {
+               DBG("Error: Tethering is required");
+               return __connman_error_permission_denied(msg);
+       }
+
+       err = start_wps(technology, msg, CONNMAN_TECHNOLOGY_WPS_AP_MODE);
+       if (err == -EINPROGRESS)
+               return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+       return __connman_error_failed(msg, -err);
+}
+
+static DBusMessage *start_sta_wps(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct connman_technology *technology = user_data;
+       int err;
+
+       if (technology->wps_reply)
+               connman_technology_reply_start_sta_wps(technology, 
-ECONNABORTED);
+
+       err = start_wps(technology, msg, CONNMAN_TECHNOLOGY_WPS_STA_MODE);
+       if (err == -EINPROGRESS) {
+               technology->wps_reply = dbus_message_ref(msg);
+               return NULL;
+       }
+
+       return __connman_error_failed(msg, -err);
+}
+
+static DBusMessage *cancel_wps(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct connman_technology *technology = user_data;
+       GSList *tech_drivers;
+       int err = 0, result = -EOPNOTSUPP;
+
+       if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
+               return __connman_error_not_supported(msg);
+
+       __sync_synchronize();
+       if (!technology->enabled)
+               return __connman_error_permission_denied(msg);
+
+       if (technology->wps_reply)
+               connman_technology_reply_start_sta_wps(technology, 
-ECONNABORTED);
+
+       for (tech_drivers = technology->driver_list; tech_drivers;
+                       tech_drivers = g_slist_next(tech_drivers)) {
+               struct connman_technology_driver *driver = tech_drivers->data;
+
+               if (!driver || !driver->cancel_wps ||
+                               driver->type != CONNMAN_SERVICE_TYPE_WIFI)
+                       continue;
+
+               err = driver->cancel_wps(technology);
+               if (result == -EINPROGRESS)
+                       continue;
+
+               if (err == -EINPROGRESS)
+                       result = err;
+       }
+
+       if (result == -EINPROGRESS)
+               return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+       return __connman_error_failed(msg, -result);
+}
+
 static DBusMessage *get_properties(DBusConnection *conn,
                                        DBusMessage *message, void *user_data)
 {
@@ -1098,6 +1314,14 @@ static const GDBusMethodTable technology_methods[] = {
                        GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
                        NULL, set_property) },
        { GDBUS_ASYNC_METHOD("Scan", NULL, NULL, scan) },
+       { GDBUS_ASYNC_METHOD("Start_AP_WPS",
+                       GDBUS_ARGS({ "authentication", "s" }),
+                       NULL, start_ap_wps) },
+       { GDBUS_ASYNC_METHOD("Start_STA_WPS",
+                       GDBUS_ARGS({ "authentication", "s" }),
+                       NULL, start_sta_wps) },
+       { GDBUS_ASYNC_METHOD("Cancel_WPS",
+                       NULL, NULL, cancel_wps) },
        { },
 };
 
-- 
2.17.1



------------------------------

Subject: Digest Footer

_______________________________________________
connman mailing list
connman@lists.01.org
https://lists.01.org/mailman/listinfo/connman


------------------------------

End of connman Digest, Vol 33, Issue 1
**************************************

Reply via email to