[connman] Added Tizen Wi-Fi Mesh 69/186569/10 submit/tizen/20180824.072908
authorNishant Chaprana <n.chaprana@samsung.com>
Fri, 10 Aug 2018 11:11:37 +0000 (16:41 +0530)
committerNishant Chaprana <n.chaprana@samsung.com>
Fri, 24 Aug 2018 04:50:11 +0000 (10:20 +0530)
Change-Id: Iec2ec99fb7d4fc6d9c0539e5a9e7c903e1969580
Signed-off-by: Nishant Chaprana <n.chaprana@samsung.com>
34 files changed:
Makefile.am [changed mode: 0755->0644]
client/commands.c [changed mode: 0755->0644]
client/dbus_helpers.c [changed mode: 0755->0644]
client/dbus_helpers.h [changed mode: 0755->0644]
client/mesh.c [new file with mode: 0644]
client/mesh.h [new file with mode: 0644]
configure.ac
gsupplicant/gsupplicant.h [changed mode: 0755->0644]
gsupplicant/supplicant.c
include/dbus.h [changed mode: 0755->0644]
include/device.h [changed mode: 0755->0644]
include/inet.h [changed mode: 0755->0644]
include/mesh-netlink.h [new file with mode: 0644]
include/mesh.h [new file with mode: 0644]
include/service.h [changed mode: 0755->0644]
packaging/connman.spec [changed mode: 0755->0644]
plugins/ethernet.c [changed mode: 0755->0644]
plugins/wifi.c [changed mode: 0755->0644]
src/config.c [changed mode: 0755->0644]
src/connman.h [changed mode: 0755->0644]
src/device.c [changed mode: 0755->0644]
src/dhcp.c [changed mode: 0755->0644]
src/error.c [changed mode: 0755->0644]
src/inet.c [changed mode: 0755->0644]
src/main.c [changed mode: 0755->0644]
src/manager.c [changed mode: 0755->0644]
src/mesh-netlink.c [new file with mode: 0644]
src/mesh.c [new file with mode: 0644]
src/notifier.c [changed mode: 0755->0644]
src/rtnl.c [changed mode: 0755->0644]
src/service.c [changed mode: 0755->0644]
src/session.c [changed mode: 0755->0644]
src/technology.c [changed mode: 0755->0644]
src/wispr.c [changed mode: 0755->0644]

old mode 100755 (executable)
new mode 100644 (file)
index 76c5419..94c6e9d
@@ -13,6 +13,10 @@ include_HEADERS = include/log.h include/plugin.h \
                        include/session.h include/ipaddress.h include/agent.h \
                        include/inotify.h include/peer.h include/machine.h
 
+if TIZEN_EXT_WIFI_MESH
+include_HEADERS += include/mesh.h include/mesh-netlink.h
+endif
+
 nodist_include_HEADERS = include/version.h
 
 noinst_HEADERS = include/rtnl.h include/task.h \
@@ -129,10 +133,18 @@ src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources) \
                        src/inotify.c src/ipv6pd.c src/peer.c \
                        src/peer_service.c src/machine.c src/util.c
 
+if TIZEN_EXT_WIFI_MESH
+src_connmand_SOURCES += src/mesh.c src/mesh-netlink.c
+endif
+
 src_connmand_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
                        @GLIB_LIBS@ @DBUS_LIBS@ @GNUTLS_LIBS@ @LIBSYSTEMD_LIBS@ \
                        -lresolv -ldl -lrt
 
+if TIZEN_EXT_WIFI_MESH
+src_connmand_LDADD += -lnl -lnl-genl
+endif
+
 src_connmand_LDFLAGS = -Wl,--export-dynamic -pie \
                                -Wl,--version-script=$(srcdir)/src/connman.ver
 
@@ -190,11 +202,19 @@ vpn_connman_vpnd_SOURCES = $(gdhcp_sources) $(builtin_vpn_sources) \
                        vpn/vpn-agent.c vpn/vpn-agent.h \
                        vpn/vpn-config.c
 
+if TIZEN_EXT_WIFI_MESH
+vpn_connman_vpnd_SOURCES += src/mesh.c src/mesh-netlink.c
+endif
+
 vpn_connman_vpnd_LDADD = gdbus/libgdbus-internal.la $(builtin_vpn_libadd) \
                                @GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ @GNUTLS_LIBS@ \
                                @LIBSYSTEMD_LIBS@ \
                                -lresolv -ldl
 
+if TIZEN_EXT_WIFI_MESH
+vpn_connman_vpnd_LDADD += -lnl -lnl-genl
+endif
+
 vpn_connman_vpnd_LDFLAGS = -Wl,--export-dynamic \
                                -Wl,--version-script=$(srcdir)/vpn/vpn.ver
 endif
@@ -314,6 +334,10 @@ client_connmanctl_SOURCES = client/dbus_helpers.h client/dbus_helpers.c \
                        client/vpnconnections.h client/vpnconnections.c \
                        client/main.c
 
+if TIZEN_EXT_WIFI_MESH
+client_connmanctl_SOURCES += client/mesh.c client/mesh.h
+endif
+
 client_connmanctl_LDADD = gdbus/libgdbus-internal.la @DBUS_LIBS@ @GLIB_LIBS@ \
                                -lreadline -ldl -lncurses
 endif
old mode 100755 (executable)
new mode 100644 (file)
index 746e158..ce82791
@@ -43,6 +43,9 @@
 #include "commands.h"
 #include "agent.h"
 #include "vpnconnections.h"
+#if defined TIZEN_EXT_WIFI_MESH
+#include "mesh.h"
+#endif
 
 static DBusConnection *connection;
 static GHashTable *service_hash;
@@ -599,6 +602,578 @@ static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering)
        return -EINPROGRESS;
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+struct mesh_if_prop {
+       char *ifname;
+       char *parent_ifname;
+       char *bridge_ifname;
+};
+
+struct mesh_create_network {
+       char *name;
+       unsigned int freq;
+       char *sec_type;
+};
+
+struct mesh_specific_scan_params {
+       char *name;
+       unsigned int freq;
+};
+
+struct mesh_gate_params {
+       bool gate_announce;
+       int hwmp_rootmode;
+       int stp;
+};
+
+static int mesh_return(DBusMessageIter *iter, const char *error,
+               void *user_data)
+{
+       char *method = user_data;
+
+       if (error)
+               fprintf(stderr, "Error %s: %s\n", method, error);
+       else
+               fprintf(stderr, "Success %s\n", method);
+
+       g_free(method);
+
+       return 0;
+}
+
+static void mesh_interface_add_append(DBusMessageIter *iter, void *user_data)
+{
+       struct mesh_if_prop *append = user_data;
+
+       /* Append Virtual Interface Name */
+               __connmanctl_dbus_append_dict_entry(iter, "Ifname",
+                               DBUS_TYPE_STRING, &append->ifname);
+
+       /* Append Parent WiFi Interface Name */
+               __connmanctl_dbus_append_dict_entry(iter, "ParentIfname",
+                               DBUS_TYPE_STRING, &append->parent_ifname);
+
+       /* Append Bridge Interface Name */
+               if (append->bridge_ifname)
+                       __connmanctl_dbus_append_dict_entry(iter, "BridgeIfname",
+                                               DBUS_TYPE_STRING, &append->bridge_ifname);
+}
+
+static void mesh_interface_remove_append(DBusMessageIter *iter, void *user_data)
+{
+       struct mesh_if_prop *append = user_data;
+
+       /* Append Virtual Interface Name */
+               __connmanctl_dbus_append_dict_entry(iter, "Ifname",
+                               DBUS_TYPE_STRING, &append->ifname);
+}
+
+static void mesh_create_network_append(DBusMessageIter *iter, void *user_data)
+{
+       struct mesh_create_network *append = user_data;
+
+       /* Append Mesh Network Name */
+               __connmanctl_dbus_append_dict_entry(iter, "Name",
+                               DBUS_TYPE_STRING, &append->name);
+
+       /* Append Mesh Network Frequency */
+               __connmanctl_dbus_append_dict_entry(iter, "Frequency",
+                               DBUS_TYPE_UINT16, &append->freq);
+
+       /* Append Mesh Network Security Type */
+               __connmanctl_dbus_append_dict_entry(iter, "Security",
+                               DBUS_TYPE_STRING, &append->sec_type);
+}
+
+static void mesh_specific_scan_append(DBusMessageIter *iter, void *user_data)
+{
+       struct mesh_specific_scan_params *append = user_data;
+
+       /* Append Mesh Network Name */
+               __connmanctl_dbus_append_dict_entry(iter, "Name",
+                               DBUS_TYPE_STRING, &append->name);
+
+       /* Append Mesh Network Frequency */
+               __connmanctl_dbus_append_dict_entry(iter, "Frequency",
+                               DBUS_TYPE_UINT16, &append->freq);
+}
+
+static void mesh_set_gate_append(DBusMessageIter *iter, void *user_data)
+{
+       struct mesh_gate_params *append = user_data;
+
+       /* Append Gate Announce Protocol */
+               __connmanctl_dbus_append_dict_entry(iter, "GateAnnounce",
+                               DBUS_TYPE_BOOLEAN, &append->gate_announce);
+
+       /* Append HWMP Root Mode */
+               __connmanctl_dbus_append_dict_entry(iter, "HWMPRootMode",
+                               DBUS_TYPE_UINT16, &append->hwmp_rootmode);
+
+       /* Append STP */
+               __connmanctl_dbus_append_dict_entry(iter, "STP", DBUS_TYPE_UINT16,
+                               &append->stp);
+}
+
+static void mesh_peer_append(DBusMessageIter *iter, void *user_data)
+{
+       char *peer_addr = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &peer_addr);
+
+       g_free(peer_addr);
+}
+
+static int mesh_peers_list(DBusMessageIter *iter,
+                                       const char *error, void *user_data)
+{
+       if (!error) {
+               __connmanctl_mesh_peers_list(iter);
+               fprintf(stdout, "\n");
+       } else
+               fprintf(stderr, "Error: %s\n", error);
+
+       return 0;
+}
+
+static int connected_mesh_peers_list(DBusMessageIter *iter,
+                                       const char *error, void *user_data)
+{
+       if (!error) {
+               __connmanctl_mesh_connected_peers_list(iter);
+               fprintf(stdout, "\n");
+       } else
+               fprintf(stderr, "Error: %s\n", error);
+
+       return 0;
+}
+
+static int disconnected_mesh_peers_list(DBusMessageIter *iter,
+                                       const char *error, void *user_data)
+{
+       if (!error) {
+               __connmanctl_mesh_disconnected_peers_list(iter);
+               fprintf(stdout, "\n");
+       } else
+               fprintf(stderr, "Error: %s\n", error);
+
+       return 0;
+}
+
+static int mesh_connect_return(DBusMessageIter *iter, const char *error,
+               void *user_data)
+{
+       char *path = user_data;
+
+       if (!error) {
+               char *str = strrchr(path, '/');
+               str++;
+               fprintf(stdout, "Connected %s\n", str);
+       } else
+               fprintf(stderr, "Error %s: %s\n", path, error);
+
+       g_free(user_data);
+
+       return 0;
+}
+
+static int mesh_disconnect_return(DBusMessageIter *iter, const char *error,
+               void *user_data)
+{
+       char *path = user_data;
+
+       if (!error) {
+               char *str = strrchr(path, '/');
+               str++;
+               fprintf(stdout, "Disconnected %s\n", str);
+       } else
+               fprintf(stderr, "Error %s: %s\n", path, error);
+
+       g_free(user_data);
+
+       return 0;
+}
+
+static int mesh_remove_return(DBusMessageIter *iter, const char *error,
+               void *user_data)
+{
+       char *path = user_data;
+
+       if (!error) {
+               char *str = strrchr(path, '/');
+               str++;
+               fprintf(stdout, "Removed %s\n", str);
+       } else
+               fprintf(stderr, "Error %s: %s\n", path, error);
+
+       g_free(user_data);
+
+       return 0;
+}
+
+static int mesh_config_return(DBusMessageIter *iter, const char *error,
+               void *user_data)
+{
+       char *path = user_data;
+       char *str = strrchr(path, '/');
+       str++;
+
+       if (error)
+               fprintf(stderr, "Error %s: %s\n", path, error);
+       else
+               fprintf(stdout, "Success SetProperty %s\n", str);
+
+       g_free(user_data);
+
+       return 0;
+}
+
+static int cmd_mesh(char *args[], int num, struct connman_option *options)
+{
+       int result = 0;
+       int c;
+       char *path = NULL;
+       char *method = NULL;
+       char *mesh_peer_name = NULL;
+       char *mesh_peer_path = NULL;
+       char *property = NULL;
+       char *value = NULL;
+       struct mesh_if_prop *append;
+       struct mesh_create_network *network;
+       struct mesh_specific_scan_params *scan_params;
+       struct mesh_gate_params *gate_params;
+       char *mesh_peer_addr = NULL;
+
+       c = parse_args(args[1], options);
+
+       switch (c) {
+       case 'a':
+               if (num < 4 || num > 5) {
+                       result = -EINVAL;
+                       break;
+               }
+               path = g_strdup_printf("/net/connman/technology/mesh");
+
+               append = dbus_malloc0(sizeof(struct mesh_if_prop));
+               append->ifname = g_strdup(args[2]);
+               append->parent_ifname = g_strdup(args[3]);
+               if (num == 5)
+                       append->bridge_ifname = g_strdup(args[4]);
+               method = g_strdup("MeshInterfaceAdd");
+               result = __connmanctl_dbus_mesh_dict(connection, path,
+                                                       "net.connman.Technology", mesh_return, method,
+                                                       "MeshInterfaceAdd", DBUS_TYPE_STRING,
+                                                       mesh_interface_add_append, append);
+               g_free(append->ifname);
+               g_free(append->parent_ifname);
+               g_free(append->bridge_ifname);
+               g_free(append);
+               break;
+
+       case 'r':
+               if (num != 3) {
+                       result = -EINVAL;
+                       break;
+               }
+               path = g_strdup_printf("/net/connman/technology/mesh");
+
+               append = dbus_malloc0(sizeof(struct mesh_if_prop));
+               append->ifname = g_strdup(args[2]);
+               method = g_strdup("MeshInterfaceRemove");
+               result = __connmanctl_dbus_mesh_dict(connection, path,
+                                                       "net.connman.Technology", mesh_return, method,
+                                                       "MeshInterfaceRemove", DBUS_TYPE_STRING,
+                                                       mesh_interface_remove_append, append);
+               g_free(append->ifname);
+               g_free(append);
+               break;
+
+       case 'p':
+               if (num > 3) {
+                       result = -E2BIG;
+                       break;
+               }
+
+               if (num == 3)
+                       mesh_peer_name = args[2];
+
+               if (!mesh_peer_name) {
+                       result = __connmanctl_dbus_method_call(connection,
+                                       CONNMAN_SERVICE, CONNMAN_PATH,
+                                       "net.connman.Manager", "GetMeshPeers",
+                                       mesh_peers_list, NULL, NULL, NULL);
+                       break;
+               }
+
+               if (check_dbus_name(mesh_peer_name) == false) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s",
+                                                                       mesh_peer_name);
+               result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
+                                               mesh_peer_path, "net.connman.Mesh", "GetProperties",
+                                               object_properties, mesh_peer_path, NULL, NULL);
+               break;
+
+       case 'c':
+               if (num < 3) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               if (num > 3) {
+                       result = -E2BIG;
+                       break;
+               }
+
+               mesh_peer_name = args[2];
+
+               if (check_dbus_name(mesh_peer_name) == false) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s",
+                                                                       mesh_peer_name);
+               result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
+                                               mesh_peer_path, "net.connman.Mesh", "Connect",
+                                               mesh_connect_return, mesh_peer_path, NULL, NULL);
+               break;
+
+       case 'd':
+               if (num < 3) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               if (num > 3) {
+                       result = -E2BIG;
+                       break;
+               }
+
+               mesh_peer_name = args[2];
+
+               if (check_dbus_name(mesh_peer_name) == false) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s",
+                                                                       mesh_peer_name);
+               result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
+                                               mesh_peer_path, "net.connman.Mesh", "Disconnect",
+                                               mesh_disconnect_return, mesh_peer_path, NULL, NULL);
+               break;
+
+       case 'f':
+               if (num < 3) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               if (num > 3) {
+                       result = -E2BIG;
+                       break;
+               }
+
+               mesh_peer_name = args[2];
+
+               if (check_dbus_name(mesh_peer_name) == false) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s",
+                                                                       mesh_peer_name);
+               result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
+                                               mesh_peer_path, "net.connman.Mesh", "Remove",
+                                               mesh_remove_return, mesh_peer_path, NULL, NULL);
+               break;
+
+       case 'C':
+               if (num > 2) {
+                       result = -E2BIG;
+                       break;
+               }
+
+               result = __connmanctl_dbus_method_call(connection,
+                                                               CONNMAN_SERVICE, CONNMAN_PATH,
+                                                               "net.connman.Manager", "GetConnectedMeshPeers",
+                                                               connected_mesh_peers_list, NULL, NULL, NULL);
+               break;
+
+       case 'D':
+               if (num > 2) {
+                       result = -E2BIG;
+                       break;
+               }
+
+               result = __connmanctl_dbus_method_call(connection,
+                                                               CONNMAN_SERVICE, CONNMAN_PATH,
+                                                               "net.connman.Manager",
+                                                               "GetDisconnectedMeshPeers",
+                                                               disconnected_mesh_peers_list, NULL, NULL, NULL);
+               break;
+
+       case 'n':
+               if (num != 5) {
+                       result = -EINVAL;
+                       break;
+               }
+               path = g_strdup_printf("/net/connman/technology/mesh");
+
+               network = dbus_malloc0(sizeof(struct mesh_create_network));
+               network->name = g_strdup(args[2]);
+               network->freq = atoi(args[3]);
+               network->sec_type = g_strdup(args[4]);
+               method = g_strdup("MeshCreateNetwork");
+               result = __connmanctl_dbus_mesh_dict(connection, path,
+                                                       "net.connman.Technology", mesh_return, method,
+                                                       "MeshCreateNetwork", DBUS_TYPE_STRING,
+                                                       mesh_create_network_append, network);
+               g_free(network->name);
+               g_free(network->sec_type);
+               g_free(network);
+               break;
+
+       case 'A':
+               if (num != 2) {
+                       result = -EINVAL;
+                       break;
+               }
+               path = g_strdup_printf("/net/connman/technology/mesh");
+
+               method = g_strdup("AbortScan");
+               result = __connmanctl_dbus_mesh_dict(connection, path,
+                                                       "net.connman.Technology", mesh_return, method,
+                                                       "AbortScan", DBUS_TYPE_STRING,
+                                                       NULL, NULL);
+               break;
+
+       case 'S':
+               if (num != 4) {
+                       result = -EINVAL;
+                       break;
+               }
+               path = g_strdup_printf("/net/connman/technology/mesh");
+
+               scan_params = dbus_malloc0(sizeof(struct mesh_specific_scan_params));
+               scan_params->name = g_strdup(args[2]);
+               scan_params->freq = atoi(args[3]);
+               method = g_strdup("MeshSpecificScan");
+               result = __connmanctl_dbus_mesh_dict(connection, path,
+                                                       "net.connman.Technology", mesh_return, method,
+                                                       "MeshSpecificScan", DBUS_TYPE_STRING,
+                                                       mesh_specific_scan_append, scan_params);
+               g_free(scan_params->name);
+               g_free(scan_params);
+               break;
+
+       case 'P':
+               if (num != 5) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               mesh_peer_name = args[2];
+               property = args[3];
+               value = args[4];
+
+               if (check_dbus_name(mesh_peer_name) == false) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s",
+                                                                       mesh_peer_name);
+
+               if (g_strcmp0(property, "Passphrase") == 0) {
+                       result = __connmanctl_dbus_set_property(connection,
+                                                               mesh_peer_path, "net.connman.Mesh",
+                                                               mesh_config_return, mesh_peer_path, property,
+                                                               DBUS_TYPE_STRING, &value);
+               } else {
+                       printf("Invalid property %s\n", property);
+                       result = -EINVAL;
+               }
+
+               break;
+
+       case 'G':
+               if (num != 5) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               path = g_strdup_printf("/net/connman/technology/mesh");
+
+               gate_params = dbus_malloc0(sizeof(struct mesh_gate_params));
+               gate_params->gate_announce = atoi(args[2]);
+               gate_params->hwmp_rootmode = atoi(args[3]);
+               gate_params->stp = atoi(args[4]);
+
+               method = g_strdup("SetMeshGate");
+
+               result = __connmanctl_dbus_mesh_dict(connection, path,
+                                                       "net.connman.Technology", mesh_return, method,
+                                                       "SetMeshGate", DBUS_TYPE_STRING,
+                                                       mesh_set_gate_append, gate_params);
+
+               break;
+
+       case 'z':
+               if (num != 3) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               mesh_peer_addr = g_strdup(args[2]);
+               method = g_strdup("MeshAddPeer");
+
+               result = __connmanctl_dbus_method_call(connection,
+                                                               CONNMAN_SERVICE, CONNMAN_PATH,
+                                                               "net.connman.Manager", "MeshAddPeer",
+                                                               mesh_return, method, mesh_peer_append,
+                                                               mesh_peer_addr);
+
+               break;
+
+       case 'y':
+               if (num != 3) {
+                       result = -EINVAL;
+                       break;
+               }
+
+               mesh_peer_addr = g_strdup(args[2]);
+               method = g_strdup("MeshRemovePeer");
+
+               result = __connmanctl_dbus_method_call(connection,
+                                                               CONNMAN_SERVICE, CONNMAN_PATH,
+                                                               "net.connman.Manager", "MeshRemovePeer",
+                                                               mesh_return, method, mesh_peer_append,
+                                                               mesh_peer_addr);
+
+               break;
+
+       default:
+               result = -EINVAL;
+               break;
+       }
+
+       g_free(path);
+
+       if (result < 0) {
+               if (result != -EINPROGRESS)
+                       printf("Error '%s': %s\n", args[1], strerror(-result));
+       }
+
+
+       return result;
+}
+#endif
+
 static int cmd_tether(char *args[], int num, struct connman_option *options)
 {
        char *ssid, *passphrase;
@@ -2251,6 +2826,38 @@ static struct connman_option session_options[] = {
        { NULL, }
 };
 
+#if defined TIZEN_EXT_WIFI_MESH
+static struct connman_option mesh_options[] = {
+       {"ifadd", 'a', "<ifname> <wifi_ifname>\n"
+               "                     [bridge_ifname]                Add Virtual Mesh "
+                       "interface"},
+       {"ifrmv", 'r', "<ifname>                       Remove Virtual Mesh "
+               "interface"},
+       {"peers", 'p', "[peer]                         Display Mesh peer "
+               "informations"},
+       {"connect", 'c', "<peer>                         Connect Mesh Peer"},
+       {"disconnect", 'd', "<peer>                         Disconnect Mesh Peer"},
+       {"remove", 'f', "<peer>                         Forget Mesh Peer"},
+       {"connected_peers", 'C', "[]                             Displays connected"
+               " Peer informations"},
+       {"disconnected_peers", 'D', "[]                           Displays "
+               "Disconnected Peer informations"},
+       {"create_network", 'n', "<name> <frequency> <sec_type>  Create New Mesh "
+               "Network"},
+       {"abort_scan", 'A', "                               Abort ongoing mesh "
+               "scan"},
+       {"specific_scan", 'S', "<name> <frequency>             Create New Mesh "
+               "Network"},
+       {"config", 'P', "<peer>                         Set Mesh Network "
+               "Configurations\n          Passphrase    <passphrase>"},
+       {"set_gate", 'G', "<gate_ann> <rootmode> <stp>    Set Mesh Gate "
+               "Option"},
+       {"add_peer", 'z', "<addr>                         Add Mesh Peer"},
+       {"remove_peer", 'y', "<addr>                         Remove Mesh Peer"},
+       { NULL, }
+};
+#endif
+
 static char *lookup_options(struct connman_option *options, const char *text,
                int state)
 {
@@ -2303,6 +2910,13 @@ static char *lookup_session(const char *text, int state)
        return lookup_options(session_options, text, state);
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+static char *lookup_mesh(const char *text, int state)
+{
+       return lookup_options(mesh_options, text, state);
+}
+#endif
+
 static int peer_service_cb(DBusMessageIter *iter, const char *error,
                                                        void *user_data)
 {
@@ -2614,6 +3228,10 @@ static const struct {
        { "disable",      "<technology>|offline", NULL,    cmd_disable,
          "Disables given technology or offline mode",
          lookup_technology_offline },
+#if defined TIZEN_EXT_WIFI_MESH
+       { "mesh",      "", mesh_options, cmd_mesh, "Mesh specific commands",
+               lookup_mesh },
+#endif
        { "tether", "<technology> on|off\n"
                    "            wifi [on|off] <ssid> <passphrase> ",
                                          NULL,            cmd_tether,
old mode 100755 (executable)
new mode 100644 (file)
index 6ca407d..8c6bdbb
@@ -224,6 +224,11 @@ static int append_variant(DBusMessageIter *iter, const char *property,
        case DBUS_TYPE_INT32:
                type_str = DBUS_TYPE_INT32_AS_STRING;
                break;
+#if defined TIZEN_EXT_WIFI_MESH
+       case DBUS_TYPE_UINT16:
+               type_str = DBUS_TYPE_UINT16_AS_STRING;
+               break;
+#endif
        default:
                return -EOPNOTSUPP;
        }
@@ -354,6 +359,50 @@ int __connmanctl_dbus_set_property_dict(DBusConnection *connection,
        return send_method_call(connection, message, cb, user_data);
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+int __connmanctl_dbus_mesh_dict(DBusConnection *connection,
+               const char *path, const char *interface,
+               connmanctl_dbus_method_return_func_t cb, void *user_data,
+               const char *property, int type,
+               connmanctl_dbus_append_func_t append_fn,
+               void *append_user_data)
+{
+       DBusMessage *message;
+       DBusMessageIter iter, variant, dict;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE, path,
+                       interface, "MeshCommands");
+
+       if (!message)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                       DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+                       &variant);
+
+       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+                       &dict);
+
+       if (append_fn)
+               append_fn(&dict, append_user_data);
+
+       dbus_message_iter_close_container(&variant, &dict);
+       dbus_message_iter_close_container(&iter, &variant);
+
+       return send_method_call(connection, message, cb, user_data);
+}
+#endif
+
 static void append_variant_array(DBusMessageIter *iter, const char *property,
                connmanctl_dbus_append_func_t append_fn,
                void *append_user_data)
old mode 100755 (executable)
new mode 100644 (file)
index 395808a..6945839
@@ -67,6 +67,15 @@ int __connmanctl_dbus_set_property_dict(DBusConnection *connection,
                connmanctl_dbus_append_func_t append_fn,
                void *append_user_data);
 
+#if defined TIZEN_EXT_WIFI_MESH
+int __connmanctl_dbus_mesh_dict(DBusConnection *connection,
+               const char *path, const char *interface,
+               connmanctl_dbus_method_return_func_t cb, void *user_data,
+               const char *property, int type,
+               connmanctl_dbus_append_func_t append_fn,
+               void *append_user_data);
+#endif
+
 void __connmanctl_dbus_append_dict_string_array(DBusMessageIter *iter,
                const char *property, connmanctl_dbus_append_func_t append_fn,
                void *append_user_data);
diff --git a/client/mesh.c b/client/mesh.c
new file mode 100644 (file)
index 0000000..6e57793
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *
+ *  Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ *  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "mesh.h"
+#include "dbus_helpers.h"
+
+static void print_mesh_peer(char *path, DBusMessageIter *iter)
+{
+       char *name = "";
+       char state = ' ';
+       char *str, *property;
+       DBusMessageIter entry, val;
+       int count = 0, favorite = 0;
+
+       while (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
+               dbus_message_iter_recurse(iter, &entry);
+               dbus_message_iter_get_basic(&entry, &property);
+
+               if (strcmp(property, "Name") == 0) {
+                       dbus_message_iter_next(&entry);
+                       dbus_message_iter_recurse(&entry, &val);
+                       dbus_message_iter_get_basic(&val, &name);
+               } else if (strcmp(property, "State") == 0) {
+                       dbus_message_iter_next(&entry);
+                       dbus_message_iter_recurse(&entry, &val);
+                       dbus_message_iter_get_basic(&val, &str);
+
+                       if (str) {
+                               if (strcmp(str, "online") == 0)
+                                       state = 'O';
+                               else if (strcmp(str, "ready") == 0)
+                                       state = 'R';
+                               else if (!strcmp(str, "association"))
+                                       state = 'a';
+                               else if (!strcmp(str, "configuration"))
+                                       state = 'c';
+                               else if (!strcmp(str, "disconnect"))
+                                       state = 'd';
+                       }
+               } else if (strcmp(property, "Favorite") == 0) {
+                       dbus_message_iter_next(&entry);
+                       dbus_message_iter_recurse(&entry, &val);
+                       dbus_message_iter_get_basic(&val, &favorite);
+               }
+
+               dbus_message_iter_next(iter);
+               count++;
+       }
+
+       str = strrchr(path, '/');
+       if (str)
+               str++;
+       else
+               str = path;
+
+       if (count > 0)
+               fprintf(stdout, "%c%c %-20s %s", favorite != 0 ? 'A' : ' ',
+                               state, name, str);
+       else
+               fprintf(stdout, "%s %s", "unchanged", str);
+}
+
+static void list_mesh_peer_array(DBusMessageIter *iter)
+{
+       DBusMessageIter array, dict;
+       char *path = NULL;
+
+       while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRUCT) {
+               dbus_message_iter_recurse(iter, &array);
+               if (dbus_message_iter_get_arg_type(&array)
+                               != DBUS_TYPE_OBJECT_PATH)
+                       return;
+
+               dbus_message_iter_get_basic(&array, &path);
+
+               dbus_message_iter_next(&array);
+               if (dbus_message_iter_get_arg_type(&array)
+                                               == DBUS_TYPE_ARRAY) {
+                       dbus_message_iter_recurse(&array, &dict);
+                       print_mesh_peer(path, &dict);
+               }
+
+               if (dbus_message_iter_has_next(iter))
+                       fprintf(stdout, "\n");
+
+               dbus_message_iter_next(iter);
+       }
+}
+
+void __connmanctl_mesh_peers_list(DBusMessageIter *iter)
+{
+       DBusMessageIter array;
+       char *path;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(iter, &array);
+       list_mesh_peer_array(&array);
+
+       dbus_message_iter_next(iter);
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return;
+
+       fprintf(stdout, "\n}, {");
+
+       dbus_message_iter_recurse(iter, &array);
+       while (dbus_message_iter_get_arg_type(&array)
+                                       == DBUS_TYPE_OBJECT_PATH) {
+               dbus_message_iter_get_basic(&array, &path);
+               fprintf(stdout, "\n%s %s", "removed", path);
+
+               dbus_message_iter_next(&array);
+       }
+
+}
+
+void __connmanctl_mesh_connected_peers_list(DBusMessageIter *iter)
+{
+       DBusMessageIter array;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(iter, &array);
+       __connmanctl_dbus_print(&array, "  ", " = ", "\n");
+       fprintf(stdout, "\n");
+}
+
+void __connmanctl_mesh_disconnected_peers_list(DBusMessageIter *iter)
+{
+       DBusMessageIter array;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(iter, &array);
+       __connmanctl_dbus_print(&array, "  ", " = ", "\n");
+       fprintf(stdout, "\n");
+}
diff --git a/client/mesh.h b/client/mesh.h
new file mode 100644 (file)
index 0000000..8dd413d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *
+ *  Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ *  02110-1301  USA
+ */
+
+#ifndef __CONNMANCTL_MESH_H
+#define __CONNMANCTL_MESH_H
+
+#include <dbus/dbus.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __connmanctl_mesh_peers_list(DBusMessageIter *iter);
+void __connmanctl_mesh_connected_peers_list(DBusMessageIter *iter);
+void __connmanctl_mesh_disconnected_peers_list(DBusMessageIter *iter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMANCTL_MESH_H */
index 01ecd95..3a569d4 100644 (file)
@@ -79,6 +79,11 @@ AC_ARG_ENABLE(tizen-ext,
                        fi])
 AM_CONDITIONAL(TIZEN_EXT, test "${enable-tizen-ext}" != "no")
 
+AC_ARG_ENABLE(tizen-ext-wifi-mesh,
+       AC_HELP_STRING([--enable-tizen-ext-wifi-mesh], [enable TIZEN extensions for Wi-Fi Mesh]),
+                       [CFLAGS="$CFLAGS -DTIZEN_EXT_WIFI_MESH"], [enable_tizen_ext_wifi_mesh="no"])
+AM_CONDITIONAL(TIZEN_EXT_WIFI_MESH, test "${enable_tizen_ext_wifi_mesh}" != "no")
+
 AC_ARG_WITH(openconnect, AC_HELP_STRING([--with-openconnect=PROGRAM],
         [specify location of openconnect binary]), [path_openconnect=${withval}])
 
old mode 100755 (executable)
new mode 100644 (file)
index 648ee57..7e94711
@@ -50,6 +50,9 @@ extern "C" {
 #define G_SUPPLICANT_CAPABILITY_MODE_IBSS              (1 << 1)
 #define G_SUPPLICANT_CAPABILITY_MODE_AP                (1 << 2)
 #define G_SUPPLICANT_CAPABILITY_MODE_P2P       (1 << 3)
+#if defined TIZEN_EXT_WIFI_MESH
+#define G_SUPPLICANT_CAPABILITY_MODE_MESH      (1 << 4)
+#endif
 
 #define G_SUPPLICANT_KEYMGMT_NONE              (1 << 0)
 #define G_SUPPLICANT_KEYMGMT_IEEE8021X (1 << 1)
@@ -66,6 +69,9 @@ extern "C" {
 #define G_SUPPLICANT_KEYMGMT_WPA_EAP   (1 << 7)
 #define G_SUPPLICANT_KEYMGMT_WPA_EAP_256       (1 << 8)
 #define G_SUPPLICANT_KEYMGMT_WPS               (1 << 9)
+#if defined TIZEN_EXT_WIFI_MESH
+#define G_SUPPLICANT_KEYMGMT_SAE               (1 << 10)
+#endif
 
 #define G_SUPPLICANT_PROTO_WPA         (1 << 0)
 #define G_SUPPLICANT_PROTO_RSN         (1 << 1)
@@ -94,8 +100,19 @@ typedef enum {
        G_SUPPLICANT_MODE_INFRA,
        G_SUPPLICANT_MODE_IBSS,
        G_SUPPLICANT_MODE_MASTER,
+#if defined TIZEN_EXT_WIFI_MESH
+       G_SUPPLICANT_MODE_MESH,
+#endif
 } GSupplicantMode;
 
+#if defined TIZEN_EXT_WIFI_MESH
+typedef enum {
+       G_SUPPLICANT_IEEE80211W_UNKNOWN,
+       G_SUPPLICANT_IEEE80211W_OPTIONAL,
+       G_SUPPLICANT_IEEE80211W_REQUIRED,
+} GSupplicantPmf;
+#endif
+
 typedef enum {
        G_SUPPLICANT_SECURITY_UNKNOWN,
        G_SUPPLICANT_SECURITY_NONE,
@@ -106,6 +123,9 @@ typedef enum {
        G_SUPPLICANT_SECURITY_FT_PSK,
        G_SUPPLICANT_SECURITY_FT_IEEE8021X,
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+       G_SUPPLICANT_SECURITY_SAE,
+#endif
 } GSupplicantSecurity;
 
 #if defined TIZEN_EXT
@@ -192,6 +212,9 @@ struct _GSupplicantSSID {
        const char *phase1;
        const char *pac_file;
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+       uint16_t ieee80211w;
+#endif
 };
 
 typedef struct _GSupplicantSSID GSupplicantSSID;
@@ -257,6 +280,9 @@ struct _GSupplicantPeer;
 
 typedef struct _GSupplicantInterface GSupplicantInterface;
 typedef struct _GSupplicantPeer GSupplicantPeer;
+#if defined TIZEN_EXT_WIFI_MESH
+typedef struct _GSupplicantMeshPeer GSupplicantMeshPeer;
+#endif
 
 typedef void (*GSupplicantInterfaceCallback) (int result,
                                        GSupplicantInterface *interface,
@@ -346,6 +372,26 @@ GSupplicantPeer *g_supplicant_interface_peer_lookup(GSupplicantInterface *interf
                                                const char *identifier);
 bool g_supplicant_interface_is_p2p_finding(GSupplicantInterface *interface);
 
+#if defined TIZEN_EXT_WIFI_MESH
+bool g_supplicant_interface_has_mesh(GSupplicantInterface *interface);
+int g_supplicant_mesh_interface_create(const char *ifname, const char *driver,
+                                               const char *bridge, const char *parent_ifname,
+                                               GSupplicantInterfaceCallback callback, void *user_data);
+const void *g_supplicant_interface_get_mesh_group_ssid(
+                                                       GSupplicantInterface *interface,
+                                                       unsigned int *ssid_len);
+int g_supplicant_mesh_get_disconnect_reason(GSupplicantInterface *interface);
+const char *g_supplicant_mesh_peer_get_address(GSupplicantMeshPeer *mesh_peer);
+int g_supplicant_mesh_peer_get_disconnect_reason(
+                                                       GSupplicantMeshPeer *mesh_peer);
+int g_supplicant_interface_abort_scan(GSupplicantInterface *interface,
+                               GSupplicantInterfaceCallback callback, void *user_data);
+int g_supplicant_interface_mesh_peer_change_status(
+                               GSupplicantInterface *interface,
+                               GSupplicantInterfaceCallback callback, const char *peer_address,
+                               const char *method, void *user_data);
+#endif
+
 /* Network and Peer API */
 struct _GSupplicantNetwork;
 struct _GSupplicantGroup;
@@ -437,6 +483,13 @@ struct _GSupplicantCallbacks {
                                int reasoncode);
        void (*assoc_status_code)(GSupplicantInterface *interface,
                                int reasoncode);
+#if defined TIZEN_EXT_WIFI_MESH
+       void (*mesh_support) (GSupplicantInterface *interface);
+       void (*mesh_group_started) (GSupplicantInterface *interface);
+       void (*mesh_group_removed) (GSupplicantInterface *interface);
+       void (*mesh_peer_connected) (GSupplicantMeshPeer *mesh_peer);
+       void (*mesh_peer_disconnected) (GSupplicantMeshPeer *mesh_peer);
+#endif
 };
 
 typedef struct _GSupplicantCallbacks GSupplicantCallbacks;
index bbaad04..4f6957b 100644 (file)
@@ -96,6 +96,9 @@ static struct strvalmap keymgmt_map[] = {
        { "wpa-eap",            G_SUPPLICANT_KEYMGMT_WPA_EAP    },
        { "wpa-eap-sha256",     G_SUPPLICANT_KEYMGMT_WPA_EAP_256        },
        { "wps",                G_SUPPLICANT_KEYMGMT_WPS                },
+#if defined TIZEN_EXT_WIFI_MESH
+       { "sae",                G_SUPPLICANT_KEYMGMT_SAE                },
+#endif
        { }
 };
 
@@ -139,6 +142,9 @@ static struct strvalmap mode_capa_map[] = {
        { "ad-hoc",             G_SUPPLICANT_CAPABILITY_MODE_IBSS       },
        { "ap",                 G_SUPPLICANT_CAPABILITY_MODE_AP         },
        { "p2p",                G_SUPPLICANT_CAPABILITY_MODE_P2P        },
+#if defined TIZEN_EXT_WIFI_MESH
+       { "mesh",               G_SUPPLICANT_CAPABILITY_MODE_MESH       },
+#endif
        { }
 };
 
@@ -162,6 +168,14 @@ struct added_network_information {
        char * private_passphrase;
 };
 
+#if defined TIZEN_EXT_WIFI_MESH
+struct _GSupplicantMeshGroupInfo {
+       unsigned char ssid[32];
+       unsigned int ssid_len;
+       int disconnect_reason;
+};
+#endif
+
 struct _GSupplicantInterface {
        char *path;
        char *network_path;
@@ -198,6 +212,10 @@ struct _GSupplicantInterface {
 #if defined TIZEN_EXT
        int disconnect_reason;
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+       bool mesh_support;
+       struct _GSupplicantMeshGroupInfo group_info;
+#endif
 };
 
 struct g_supplicant_bss {
@@ -230,6 +248,9 @@ struct g_supplicant_bss {
        unsigned char country_code[COUNTRY_CODE_LENGTH];
 #endif
        unsigned int wps_capabilities;
+#if defined TIZEN_EXT_WIFI_MESH
+       dbus_bool_t sae;
+#endif
 };
 
 struct _GSupplicantNetwork {
@@ -297,6 +318,10 @@ struct interface_create_data {
        char *ifname;
        char *driver;
        char *bridge;
+#if defined TIZEN_EXT_WIFI_MESH
+       char *parent_ifname;
+       bool is_mesh_interface;
+#endif
        GSupplicantInterface *interface;
        GSupplicantInterfaceCallback callback;
        void *user_data;
@@ -331,6 +356,14 @@ struct g_connman_bssids {
 
 static int network_remove(struct interface_data *data);
 
+#if defined TIZEN_EXT_WIFI_MESH
+struct _GSupplicantMeshPeer {
+       GSupplicantInterface *interface;
+       char *peer_address;
+       int disconnect_reason;
+};
+#endif
+
 static inline void debug(const char *format, ...)
 {
        char str[256];
@@ -359,6 +392,10 @@ static GSupplicantMode string2mode(const char *mode)
                return G_SUPPLICANT_MODE_INFRA;
        else if (g_str_equal(mode, "ad-hoc"))
                return G_SUPPLICANT_MODE_IBSS;
+#if defined TIZEN_EXT_WIFI_MESH
+       else if (g_str_equal(mode, "mesh"))
+               return G_SUPPLICANT_MODE_MESH;
+#endif
 
        return G_SUPPLICANT_MODE_UNKNOWN;
 }
@@ -374,6 +411,10 @@ static const char *mode2string(GSupplicantMode mode)
                return "adhoc";
        case G_SUPPLICANT_MODE_MASTER:
                return "ap";
+#if defined TIZEN_EXT_WIFI_MESH
+       case G_SUPPLICANT_MODE_MESH:
+               return "mesh";
+#endif
        }
 
        return NULL;
@@ -398,6 +439,10 @@ static const char *security2string(GSupplicantSecurity security)
        case G_SUPPLICANT_SECURITY_FT_IEEE8021X:
                return "ft_ieee8021x";
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+       case G_SUPPLICANT_SECURITY_SAE:
+               return "sae";
+#endif
        }
 
        return NULL;
@@ -580,6 +625,27 @@ static void callback_p2p_support(GSupplicantInterface *interface)
 }
 #endif
 
+#if defined TIZEN_EXT_WIFI_MESH
+static void callback_mesh_support(GSupplicantInterface *interface)
+{
+       SUPPLICANT_DBG("");
+
+       if (!interface->mesh_support)
+               return;
+
+       if (callbacks_pointer && callbacks_pointer->mesh_support)
+               callbacks_pointer->mesh_support(interface);
+}
+
+bool g_supplicant_interface_has_mesh(GSupplicantInterface *interface)
+{
+       if (!interface)
+               return false;
+
+       return interface->mesh_support;
+}
+#endif
+
 static void callback_scan_started(GSupplicantInterface *interface)
 {
        if (!callbacks_pointer)
@@ -1698,6 +1764,10 @@ static void merge_network(GSupplicantNetwork *network)
                g_string_append_printf(str, "_managed");
        else if (g_strcmp0(mode, "1") == 0)
                g_string_append_printf(str, "_adhoc");
+#if defined TIZEN_EXT_WIFI_MESH
+       else if (g_strcmp0(mode, "5") == 0)
+               g_string_append_printf(str, "_mesh");
+#endif
 
        if (g_strcmp0(key_mgmt, "WPA-PSK") == 0)
                g_string_append_printf(str, "_psk");
@@ -2266,6 +2336,11 @@ static void bss_compute_security(struct g_supplicant_bss *bss)
                bss->psk = TRUE;
 #endif
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (bss->keymgmt & G_SUPPLICANT_KEYMGMT_SAE)
+               bss->sae = TRUE;
+#endif
+
        if (bss->ieee8021x)
                bss->security = G_SUPPLICANT_SECURITY_IEEE8021X;
        else if (bss->psk)
@@ -2276,6 +2351,10 @@ static void bss_compute_security(struct g_supplicant_bss *bss)
        else if (bss->ft_ieee8021x == TRUE)
                bss->security = G_SUPPLICANT_SECURITY_IEEE8021X;
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+       else if (bss->sae)
+               bss->security = G_SUPPLICANT_SECURITY_SAE;
+#endif
        else if (bss->privacy)
                bss->security = G_SUPPLICANT_SECURITY_WEP;
        else
@@ -2672,6 +2751,10 @@ static void interface_property(const char *key, DBusMessageIter *iter,
                if (interface->mode_capa & G_SUPPLICANT_CAPABILITY_MODE_P2P)
                        interface->p2p_support = true;
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+               if (interface->mode_capa & G_SUPPLICANT_CAPABILITY_MODE_MESH)
+                       interface->mesh_support = true;
+#endif
        } else if (g_strcmp0(key, "State") == 0) {
                const char *str = NULL;
 
@@ -3969,6 +4052,214 @@ static void signal_group_peer_disconnected(const char *path, DBusMessageIter *it
        peer->connection_requested = false;
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+const void *g_supplicant_interface_get_mesh_group_ssid(
+                                                       GSupplicantInterface *interface,
+                                                       unsigned int *ssid_len)
+{
+       if (!ssid_len)
+               return NULL;
+
+       if (!interface || interface->group_info.ssid_len == 0) {
+               *ssid_len = 0;
+               return NULL;
+       }
+
+       *ssid_len = interface->group_info.ssid_len;
+       return interface->group_info.ssid;
+}
+
+int g_supplicant_mesh_get_disconnect_reason(GSupplicantInterface *interface)
+{
+       if (!interface)
+               return -EINVAL;
+
+       return interface->group_info.disconnect_reason;
+}
+
+const char *g_supplicant_mesh_peer_get_address(GSupplicantMeshPeer *mesh_peer)
+{
+       if (!mesh_peer || !mesh_peer->peer_address)
+               return NULL;
+
+       return mesh_peer->peer_address;
+}
+
+int g_supplicant_mesh_peer_get_disconnect_reason(GSupplicantMeshPeer *mesh_peer)
+{
+       if (!mesh_peer)
+               return -EINVAL;
+
+       return mesh_peer->disconnect_reason;
+}
+
+static void callback_mesh_group_started(GSupplicantInterface *interface)
+{
+       if (!callbacks_pointer)
+               return;
+
+       if (!callbacks_pointer->mesh_group_started)
+               return;
+
+       callbacks_pointer->mesh_group_started(interface);
+}
+
+static void callback_mesh_group_removed(GSupplicantInterface *interface)
+{
+       if (!callbacks_pointer)
+               return;
+
+       if (!callbacks_pointer->mesh_group_removed)
+               return;
+
+       callbacks_pointer->mesh_group_removed(interface);
+}
+
+static void mesh_group_info(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       if (!key)
+               return;
+
+       if (g_strcmp0(key, "SSID") == 0) {
+               DBusMessageIter array;
+               unsigned char *ssid;
+               int ssid_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
+
+               if (ssid_len > 0 && ssid_len < 33) {
+                       memcpy(interface->group_info.ssid, ssid, ssid_len);
+                       interface->group_info.ssid_len = ssid_len;
+               } else {
+                       memset(interface->group_info.ssid, 0, 32);
+                       interface->group_info.ssid_len = 0;
+               }
+       } else if (g_strcmp0(key, "DisconnectReason") == 0) {
+               int disconnect_reason = 0;
+               dbus_message_iter_get_basic(iter, &disconnect_reason);
+               interface->group_info.disconnect_reason = disconnect_reason;
+       }
+}
+
+static void signal_mesh_group_started(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (!interface)
+               return;
+
+       supplicant_dbus_property_foreach(iter, mesh_group_info, interface);
+
+       callback_mesh_group_started(interface);
+}
+
+static void signal_mesh_group_removed(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (!interface)
+               return;
+
+       supplicant_dbus_property_foreach(iter, mesh_group_info, interface);
+
+       callback_mesh_group_removed(interface);
+}
+
+static void callback_mesh_peer_connected(GSupplicantMeshPeer *mesh_peer)
+{
+       if (!callbacks_pointer)
+               return;
+
+       if (!callbacks_pointer->mesh_peer_connected)
+               return;
+
+       callbacks_pointer->mesh_peer_connected(mesh_peer);
+}
+
+static void callback_mesh_peer_disconnected(GSupplicantMeshPeer *mesh_peer)
+{
+       if (!callbacks_pointer)
+               return;
+
+       if (!callbacks_pointer->mesh_peer_disconnected)
+               return;
+
+       callbacks_pointer->mesh_peer_disconnected(mesh_peer);
+}
+
+static void mesh_peer_info(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       GSupplicantMeshPeer *mesh_peer = user_data;
+       if (!key)
+               return;
+
+       if (g_strcmp0(key, "PeerAddress") == 0) {
+               DBusMessageIter array;
+               unsigned char *addr;
+               int addr_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &addr, &addr_len);
+
+               if (addr_len == 6) {
+                       mesh_peer->peer_address = g_malloc0(19);
+                       snprintf(mesh_peer->peer_address, 19,
+                                        "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1],
+                                        addr[2], addr[3], addr[4], addr[5]);
+               }
+       } else if (g_strcmp0(key, "DisconnectReason") == 0) {
+               int disconnect_reason = 0;
+               dbus_message_iter_get_basic(iter, &disconnect_reason);
+               mesh_peer->disconnect_reason = disconnect_reason;
+       }
+}
+
+static void signal_mesh_peer_connected(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+       GSupplicantMeshPeer *mesh_peer;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (!interface)
+               return;
+
+       mesh_peer = dbus_malloc0(sizeof(GSupplicantMeshPeer));
+       mesh_peer->interface = interface;
+
+       supplicant_dbus_property_foreach(iter, mesh_peer_info, mesh_peer);
+
+       callback_mesh_peer_connected(mesh_peer);
+       g_free(mesh_peer->peer_address);
+       g_free(mesh_peer);
+}
+
+static void signal_mesh_peer_disconnected(const char *path,
+                                                               DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+       GSupplicantMeshPeer *mesh_peer;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (!interface)
+               return;
+
+       mesh_peer = dbus_malloc0(sizeof(GSupplicantMeshPeer));
+       mesh_peer->interface = interface;
+
+       supplicant_dbus_property_foreach(iter, mesh_peer_info, mesh_peer);
+
+       callback_mesh_peer_disconnected(mesh_peer);
+       g_free(mesh_peer->peer_address);
+       g_free(mesh_peer);
+}
+#endif
+
 static struct {
        const char *interface;
        const char *member;
@@ -4012,6 +4303,16 @@ static struct {
 
        { SUPPLICANT_INTERFACE ".Group", "PeerJoined", signal_group_peer_joined },
        { SUPPLICANT_INTERFACE ".Group", "PeerDisconnected", signal_group_peer_disconnected },
+#if defined TIZEN_EXT_WIFI_MESH
+       { SUPPLICANT_INTERFACE ".Interface.Mesh", "MeshGroupStarted",
+               signal_mesh_group_started },
+       { SUPPLICANT_INTERFACE ".Interface.Mesh", "MeshGroupRemoved",
+               signal_mesh_group_removed },
+       { SUPPLICANT_INTERFACE ".Interface.Mesh", "MeshPeerConnected",
+               signal_mesh_peer_connected },
+       { SUPPLICANT_INTERFACE ".Interface.Mesh", "MeshPeerDisconnected",
+               signal_mesh_peer_disconnected },
+#endif
 
        { }
 };
@@ -4290,6 +4591,9 @@ static void interface_create_data_free(struct interface_create_data *data)
        g_free(data->ifname);
        g_free(data->driver);
        g_free(data->bridge);
+#if defined TIZEN_EXT_WIFI_MESH
+       g_free(data->parent_ifname);
+#endif
        dbus_free(data);
 }
 
@@ -4317,6 +4621,9 @@ static void interface_create_property(const char *key, DBusMessageIter *iter,
 #if !defined TIZEN_EXT
                        callback_p2p_support(interface);
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+                       callback_mesh_support(interface);
+#endif
                }
 
                interface_create_data_free(data);
@@ -4403,6 +4710,17 @@ static void interface_create_params(DBusMessageIter *iter, void *user_data)
                                        DBUS_TYPE_STRING, &config_file);
        }
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (data->is_mesh_interface) {
+               if (data->parent_ifname)
+                       supplicant_dbus_dict_append_basic(&dict, "ParentIfname",
+                                       DBUS_TYPE_STRING, &data->parent_ifname);
+
+               supplicant_dbus_dict_append_basic(&dict, "IsMeshInterface",
+                                       DBUS_TYPE_BOOLEAN, &data->is_mesh_interface);
+       }
+#endif
+
        supplicant_dbus_dict_close(iter, &dict);
 }
 
@@ -4438,6 +4756,9 @@ static void interface_get_result(const char *error,
 #if !defined TIZEN_EXT
                callback_p2p_support(interface);
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+               callback_mesh_support(interface);
+#endif
        }
 
        interface_create_data_free(data);
@@ -4477,6 +4798,117 @@ static void interface_get_params(DBusMessageIter *iter, void *user_data)
        dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data->ifname);
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+int g_supplicant_mesh_interface_create(const char *ifname, const char *driver,
+                                               const char *bridge, const char *parent_ifname,
+                                               GSupplicantInterfaceCallback callback, void *user_data)
+{
+       struct interface_create_data *data;
+       int ret;
+
+       SUPPLICANT_DBG("ifname %s", ifname);
+
+       if (!ifname || !parent_ifname)
+               return -EINVAL;
+
+       if (!system_available)
+               return -EFAULT;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (!data)
+               return -ENOMEM;
+
+       data->ifname = g_strdup(ifname);
+       data->driver = g_strdup(driver);
+       data->bridge = g_strdup(bridge);
+       data->is_mesh_interface = true;
+       data->parent_ifname = g_strdup(parent_ifname);
+       data->callback = callback;
+       data->user_data = user_data;
+
+       ret = supplicant_dbus_method_call(SUPPLICANT_PATH,
+                                               SUPPLICANT_INTERFACE,
+                                               "CreateInterface",
+                                               interface_create_params,
+                                               interface_create_result, data,
+                                               NULL);
+       return ret;
+}
+
+struct interface_mesh_peer_data {
+       char *peer_address;
+       char *method;
+       GSupplicantInterface *interface;
+       GSupplicantInterfaceCallback callback;
+       void *user_data;
+};
+
+static void interface_mesh_change_peer_params(DBusMessageIter *iter,
+                                                                                  void *user_data)
+{
+       struct interface_mesh_peer_data *data = user_data;
+
+       SUPPLICANT_DBG("");
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data->peer_address);
+}
+
+static void interface_mesh_change_peer_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_mesh_peer_data *data = user_data;
+       int err = 0;
+
+       SUPPLICANT_DBG("%s", data->method);
+
+       if (error) {
+               err = -EIO;
+               SUPPLICANT_DBG("error %s", error);
+       }
+
+       if (data->callback)
+               data->callback(err, data->interface, data->user_data);
+
+       g_free(data->peer_address);
+       g_free(data->method);
+       dbus_free(data);
+}
+
+int g_supplicant_interface_mesh_peer_change_status(
+                               GSupplicantInterface *interface,
+                               GSupplicantInterfaceCallback callback, const char *peer_address,
+                               const char *method, void *user_data)
+{
+       struct interface_mesh_peer_data *data;
+       int ret;
+
+       if (!peer_address)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (!data)
+               return -ENOMEM;
+
+       data->peer_address = g_strdup(peer_address);
+       data->method = g_strdup(method);
+       data->interface = interface;
+       data->callback = callback;
+       data->user_data = user_data;
+
+       ret = supplicant_dbus_method_call(interface->path,
+                                               SUPPLICANT_INTERFACE ".Interface.Mesh",
+                                               method, interface_mesh_change_peer_params,
+                                               interface_mesh_change_peer_result, data, NULL);
+       if (ret < 0) {
+               g_free(data->peer_address);
+               g_free(data->method);
+               dbus_free(data);
+       }
+
+       return ret;
+}
+#endif
+
 int g_supplicant_interface_create(const char *ifname, const char *driver,
                                        const char *bridge,
                                        GSupplicantInterfaceCallback callback,
@@ -4787,6 +5219,57 @@ static int interface_ready_to_scan(GSupplicantInterface *interface)
        return 0;
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+static void interface_abort_scan_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_scan_data *data = user_data;
+       int err = 0;
+
+       if (error) {
+               SUPPLICANT_DBG("error %s", error);
+               err = -EIO;
+       }
+
+       g_free(data->path);
+
+               if (data->callback)
+                       data->callback(err, data->interface, data->user_data);
+
+       dbus_free(data);
+}
+
+int g_supplicant_interface_abort_scan(GSupplicantInterface *interface,
+                               GSupplicantInterfaceCallback callback, void *user_data)
+{
+       struct interface_scan_data *data;
+       int ret;
+
+       if (!interface->scanning)
+               return -EEXIST;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (!data)
+               return -ENOMEM;
+
+       data->interface = interface;
+       data->path = g_strdup(interface->path);
+       data->callback = callback;
+       data->user_data = user_data;
+
+       ret = supplicant_dbus_method_call(interface->path,
+                       SUPPLICANT_INTERFACE ".Interface", "AbortScan", NULL,
+                       interface_abort_scan_result, data, interface);
+
+       if (ret < 0) {
+               g_free(data->path);
+               dbus_free(data);
+       }
+
+       return ret;
+}
+#endif
+
 int g_supplicant_interface_scan(GSupplicantInterface *interface,
                                GSupplicantScanParams *scan_data,
                                GSupplicantInterfaceCallback callback,
@@ -5429,6 +5912,17 @@ static void add_network_security_proto(DBusMessageIter *dict,
        g_free(proto);
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+static void add_network_ieee80211w(DBusMessageIter *dict, GSupplicantSSID *ssid)
+{
+       if (ssid->security != G_SUPPLICANT_SECURITY_SAE)
+               return;
+
+       supplicant_dbus_dict_append_basic(dict, "ieee80211w", DBUS_TYPE_UINT32,
+                                         &ssid->ieee80211w);
+}
+#endif
+
 static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
 {
        char *key_mgmt;
@@ -5471,6 +5965,12 @@ static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
                add_network_security_proto(dict, ssid);
                break;
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+       case G_SUPPLICANT_SECURITY_SAE:
+               key_mgmt = "SAE";
+               add_network_security_psk(dict, ssid);
+               break;
+#endif
        }
 
        supplicant_dbus_dict_append_basic(dict, "key_mgmt",
@@ -5492,6 +5992,11 @@ static void add_network_mode(DBusMessageIter *dict, GSupplicantSSID *ssid)
        case G_SUPPLICANT_MODE_MASTER:
                mode = 2;
                break;
+#if defined TIZEN_EXT_WIFI_MESH
+       case G_SUPPLICANT_MODE_MESH:
+               mode = 5;
+               break;
+#endif
        }
 
        supplicant_dbus_dict_append_basic(dict, "mode",
@@ -5522,6 +6027,10 @@ static void interface_add_network_params(DBusMessageIter *iter, void *user_data)
 
        add_network_security(&dict, ssid);
 
+#if defined TIZEN_EXT_WIFI_MESH
+       add_network_ieee80211w(&dict, ssid);
+#endif
+
        supplicant_dbus_dict_append_fixed_array(&dict, "ssid",
                                        DBUS_TYPE_BYTE, &ssid->ssid,
                                                ssid->ssid_len);
@@ -5865,7 +6374,12 @@ int g_supplicant_interface_connect(GSupplicantInterface *interface,
                        network_remove(intf_data);
                } else
 #if defined TIZEN_EXT
-                       if (ssid->passphrase && g_strcmp0(ssid->passphrase, "") != 0 && !ssid->eap) {
+                       if (ssid->passphrase &&
+                           g_strcmp0(ssid->passphrase, "") != 0 &&
+#if defined TIZEN_EXT_WIFI_MESH
+                           ssid->mode != G_SUPPLICANT_MODE_MESH &&
+#endif
+                           !ssid->eap) {
                                ret = send_decryption_request(ssid->passphrase, data);
                                if (ret < 0)
                                        SUPPLICANT_DBG("Decryption request failed %d", ret);
@@ -6549,6 +7063,10 @@ static const char *g_supplicant_rule7 = "type=signal,"
 static const char *g_supplicant_rule8 = "type=signal,"
                "interface=" SUPPLICANT_INTERFACE ".Group";
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+static const char *g_supplicant_rule9 = "type=signal,"
+               "interface=" SUPPLICANT_INTERFACE ".Interface.Mesh";
+#endif
 
 static void invoke_introspect_method(void)
 {
@@ -6615,6 +7133,9 @@ int g_supplicant_register(const GSupplicantCallbacks *callbacks)
        dbus_bus_add_match(connection, g_supplicant_rule7, NULL);
        dbus_bus_add_match(connection, g_supplicant_rule8, NULL);
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+       dbus_bus_add_match(connection, g_supplicant_rule9, NULL);
+#endif
        dbus_connection_flush(connection);
 
        if (dbus_bus_name_has_owner(connection,
@@ -6656,6 +7177,9 @@ void g_supplicant_unregister(const GSupplicantCallbacks *callbacks)
        SUPPLICANT_DBG("");
 
        if (connection) {
+#if defined TIZEN_EXT_WIFI_MESH
+               dbus_bus_remove_match(connection, g_supplicant_rule9, NULL);
+#endif
 #if !defined TIZEN_EXT
                dbus_bus_remove_match(connection, g_supplicant_rule8, NULL);
                dbus_bus_remove_match(connection, g_supplicant_rule7, NULL);
old mode 100755 (executable)
new mode 100644 (file)
index 3087fe4..19e4938
@@ -47,6 +47,9 @@ extern "C" {
 #define CONNMAN_SESSION_INTERFACE      CONNMAN_SERVICE ".Session"
 #define CONNMAN_NOTIFICATION_INTERFACE CONNMAN_SERVICE ".Notification"
 #define CONNMAN_PEER_INTERFACE         CONNMAN_SERVICE ".Peer"
+#if defined TIZEN_EXT_WIFI_MESH
+#define CONNMAN_MESH_INTERFACE         CONNMAN_SERVICE ".Mesh"
+#endif
 
 #define CONNMAN_PRIVILEGE_MODIFY       1
 #define CONNMAN_PRIVILEGE_SECRET       2
old mode 100755 (executable)
new mode 100644 (file)
index 36b2f55..852207d
@@ -131,6 +131,13 @@ struct connman_device_driver {
                        struct connman_device *device, int scan_type,
                        GSList *specific_scan_list, void *user_data);
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+       int (*abort_scan) (enum connman_service_type type,
+                                               struct connman_device *device);
+       int (*mesh_specific_scan) (enum connman_service_type type,
+                       struct connman_device *device, const char *ssid, unsigned int freq,
+                       void *user_data);
+#endif
 };
 
 int connman_device_driver_register(struct connman_device_driver *driver);
old mode 100755 (executable)
new mode 100644 (file)
index 0742730..ef61af9
@@ -81,6 +81,11 @@ int connman_inet_check_ipaddress(const char *host);
 bool connman_inet_check_hostname(const char *ptr, size_t len);
 bool connman_inet_is_ipv6_supported();
 
+#if defined TIZEN_EXT_WIFI_MESH
+char *connman_inet_ifaddr(const char *name);
+char *connman_inet_ifname2addr(const char *name);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/mesh-netlink.h b/include/mesh-netlink.h
new file mode 100644 (file)
index 0000000..78b3e36
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *
+ *  Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ *  02110-1301  USA
+ */
+
+#ifndef __CONNMAN_MESH_NETLINK_H
+#define __CONNMAN_MESH_NETLINK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+       int id;
+       struct nl_sock *nl_socket;
+       struct nl_cb *cb;
+} mesh_nl80211_global;
+
+#define MESH_HWMP_ROOTMODE_NO_ROOT                                             0
+#define MESH_HWMP_ROOTMODE_PROACTIVE_PREQ_NO_PREP              2
+#define MESH_HWMP_ROOTMODE_PROACTIVE_PREQ_WITH_PREP            3
+#define MESH_HWMP_ROOTMODE_RANN                                                        4
+
+#define NL80211_ATTR_IFINDEX                                                   3
+#define NL80211_CMD_SET_MESH_CONFIG                                            29
+#define NL80211_ATTR_MESH_CONFIG                                               35
+
+#define NL80211_MESHCONF_HWMP_ROOTMODE                                 14
+#define NL80211_MESHCONF_GATE_ANNOUNCEMENTS                            17
+
+int __connman_mesh_netlink_set_gate_announce(mesh_nl80211_global *global,
+                                       int mesh_if_index, bool gate_announce, int hwmp_rootmode);
+
+mesh_nl80211_global *__connman_mesh_nl80211_global_init(void);
+void __connman_mesh_nl80211_global_deinit(mesh_nl80211_global *global);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_MESH_NETLINK_H */
diff --git a/include/mesh.h b/include/mesh.h
new file mode 100644 (file)
index 0000000..547c237
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *
+ *  Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ *  02110-1301  USA
+ */
+
+#ifndef __CONNMAN_MESH_H
+#define __CONNMAN_MESH_H
+
+#include <gdbus.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct connman_mesh;
+
+enum connman_mesh_security {
+       CONNMAN_MESH_SECURITY_UNKNOWN = 0,
+       CONNMAN_MESH_SECURITY_NONE    = 1,
+       CONNMAN_MESH_SECURITY_SAE     = 2,
+};
+
+enum connman_mesh_state {
+       CONNMAN_MESH_STATE_UNKNOWN       = 0,
+       CONNMAN_MESH_STATE_IDLE          = 1,
+       CONNMAN_MESH_STATE_ASSOCIATION   = 2,
+       CONNMAN_MESH_STATE_CONFIGURATION = 3,
+       CONNMAN_MESH_STATE_READY         = 4,
+       CONNMAN_MESH_STATE_DISCONNECT    = 5,
+       CONNMAN_MESH_STATE_FAILURE       = 6,
+};
+
+enum connman_mesh_peer_type {
+       CONNMAN_MESH_PEER_TYPE_CREATED    = 0,
+       CONNMAN_MESH_PEER_TYPE_DISCOVERED = 1,
+};
+
+enum connman_mesh_peer_disconnect_reason {
+       CONNMAN_MESH_REASON_UNKNOWN          = 0,
+       CONNMAN_MESH_DEAUTH_LEAVING          = 1,
+       CONNMAN_MESH_PEERING_CANCELLED       = 2,
+       CONNMAN_MESH_MAX_PEERS               = 3,
+       CONNMAN_MESH_CONFIG_POLICY_VIOLATION = 4,
+       CONNMAN_MESH_CLOSE_RCVD              = 5,
+       CONNMAN_MESH_MAX_RETRIES             = 6,
+       CONNMAN_MESH_CONFIRM_TIMEOUT         = 7,
+       CONNMAN_MESH_INVALID_GTK             = 8,
+       CONNMAN_MESH_INCONSISTENT_PARAMS     = 9,
+       CONNMAN_MESH_INVALID_SECURITY_CAP    = 10,
+};
+
+enum connman_mesh_peer_status {
+       CONNMAN_MESH_PEER_ADD    = 0,
+       CONNMAN_MESH_PEER_REMOVE = 1,
+};
+
+struct connman_mesh *connman_mesh_create(const char *interface_addr,
+                                                                       const char *identifier);
+
+void connman_mesh_set_name(struct connman_mesh *mesh, const char *name);
+const char *connman_mesh_get_name(struct connman_mesh *mesh);
+void connman_mesh_set_passphrase(struct connman_mesh *mesh,
+                                                                const char *passphrase);
+const char *connman_mesh_get_passphrase(struct connman_mesh *mesh);
+void connman_mesh_set_address(struct connman_mesh *mesh, const char *address);
+void connman_mesh_set_security(struct connman_mesh *mesh, const char *security);
+const char *connman_mesh_get_security(struct connman_mesh *mesh);
+void connman_mesh_set_frequency(struct connman_mesh *mesh, uint16_t frequency);
+uint16_t connman_mesh_get_frequency(struct connman_mesh *mesh);
+void connman_mesh_set_ieee80211w(struct connman_mesh *mesh, uint16_t ieee80211w);
+uint16_t connman_mesh_get_ieee80211w(struct connman_mesh *mesh);
+int connman_mesh_peer_set_state(struct connman_mesh *mesh,
+                                                               enum connman_mesh_state new_state);
+void connman_mesh_set_peer_type(struct connman_mesh *mesh,
+                                                               enum connman_mesh_peer_type type);
+bool connman_mesh_peer_is_connected_state(struct connman_mesh *mesh);
+struct connman_mesh *connman_get_connected_mesh_from_name(char *name);
+struct connman_mesh *connman_get_connecting_mesh_from_name(char *name);
+void connman_mesh_set_index(struct connman_mesh *mesh, int index);
+void connman_mesh_set_strength(struct connman_mesh *mesh, uint8_t strength);
+void connman_mesh_peer_set_disconnect_reason(struct connman_mesh *mesh,
+                                               int disconnect_reason);
+void __connman_mesh_add_ethernet_to_bridge(void);
+void __connman_mesh_remove_ethernet_from_bridge(void);
+int __connman_mesh_change_peer_status(DBusMessage *msg,
+                                      const char *peer_address,
+                                      enum connman_mesh_peer_status status);
+
+int connman_mesh_register(struct connman_mesh *mesh);
+void connman_mesh_unregister(struct connman_mesh *mesh);
+
+int __connman_mesh_add_virtual_interface(const char *ifname,
+                                         const char *parent_ifname, const char *bridge_ifname);
+
+int __connman_mesh_remove_virtual_interface(const char *ifname);
+int __connman_mesh_set_stp_gate_announce(bool gate_announce, int hwmp_rootmode,
+                                         int stp);
+
+const char *connman_mesh_get_interface_name(void);
+bool connman_mesh_is_interface_created(void);
+
+struct connman_mesh *connman_mesh_get(const char *interface_addr,
+                                      const char *identifier);
+
+int connman_mesh_notify_interface_create(bool success);
+int connman_mesh_notify_interface_remove(bool success);
+
+int connman_mesh_add_connected_peer(const char *peer_address);
+int connman_mesh_remove_connected_peer(const char *peer_address, int reason);
+
+typedef void (*mesh_change_peer_status_cb_t) (int result, void *user_data);
+
+struct connman_mesh_driver {
+       int (*add_interface) (const char *ifname, const char *parent_ifname);
+       int (*remove_interface) (const char *ifname);
+       int (*connect) (struct connman_mesh *mesh);
+       int (*disconnect) (struct connman_mesh *mesh);
+       int (*change_peer_status) (const char *peer_address,
+                                               enum connman_mesh_peer_status status,
+                                               mesh_change_peer_status_cb_t callback, void *user_data);
+};
+
+int connman_mesh_driver_register(struct connman_mesh_driver *driver);
+void connman_mesh_driver_unregister(struct connman_mesh_driver *driver);
+
+struct connman_mesh_eth_driver {
+       int (*add_to_bridge) (const char *bridge);
+       int (*remove_from_bridge) (const char *bridge);
+};
+
+int connman_mesh_eth_driver_register(struct connman_mesh_eth_driver *driver);
+void connman_mesh_eth_driver_unregister(struct connman_mesh_eth_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_MESH_H */
old mode 100755 (executable)
new mode 100644 (file)
index 3055023..961b594
@@ -49,8 +49,15 @@ enum connman_service_type {
        CONNMAN_SERVICE_TYPE_VPN       = 7,
        CONNMAN_SERVICE_TYPE_GADGET    = 8,
        CONNMAN_SERVICE_TYPE_P2P       = 9,
+#if defined TIZEN_EXT_WIFI_MESH
+       CONNMAN_SERVICE_TYPE_MESH      = 10,
+#endif
 };
+#if defined TIZEN_EXT_WIFI_MESH
+#define        MAX_CONNMAN_SERVICE_TYPES        11
+#else
 #define        MAX_CONNMAN_SERVICE_TYPES        10
+#endif
 
 
 enum connman_service_security {
old mode 100755 (executable)
new mode 100644 (file)
index 411a651..fd69829
@@ -5,7 +5,7 @@
 
 Name:           connman
 Version:        1.35
-Release:        27
+Release:        28
 License:        GPL-2.0+
 Summary:        Connection Manager
 Url:            http://connman.net
@@ -18,6 +18,7 @@ BuildRequires:  pkgconfig(gio-2.0)
 BuildRequires:  pkgconfig(libiptc)
 BuildRequires:  pkgconfig(xtables)
 BuildRequires:  pkgconfig(libsmack)
+BuildRequires:  pkgconfig(libnl-2.0)
 BuildRequires:  pkgconfig(libsystemd-daemon)
 %if %{with connman_openconnect}
 BuildRequires:  openconnect
@@ -141,8 +142,6 @@ This overwrites conf file of %{name}.
 
 
 %build
-#CFLAGS+=" -DTIZEN_EXT -lsmack -Werror"
-
 %if %{with connman_vpnd}
 VPN_CFLAGS+=" -DTIZEN_EXT -lsmack -Werror"
 %endif
@@ -151,9 +150,9 @@ chmod +x bootstrap
 ./bootstrap
 %configure \
             --sysconfdir=/etc \
-            --enable-client \
-                       --enable-tizen-ext \
-            --enable-pacrunner \
+           --enable-client \
+           --enable-tizen-ext \
+           --enable-pacrunner \
             --enable-wifi=builtin \
 %if %{with connman_openconnect}
             --enable-openconnect \
@@ -170,11 +169,11 @@ chmod +x bootstrap
             --disable-ofono \
             --enable-telephony=builtin \
             --enable-test \
-                       --enable-loopback \
-                       --enable-ethernet \
-            --with-systemdunitdir=%{_libdir}/systemd/system \
-            --enable-pie \
-                       --disable-wispr
+           --enable-loopback \
+           --enable-ethernet \
+           --with-systemdunitdir=%{_libdir}/systemd/system \
+           --enable-pie \
+           --disable-wispr
 
 make %{?_smp_mflags}
 
old mode 100755 (executable)
new mode 100644 (file)
index 9a4d741..aadfe89
@@ -48,6 +48,9 @@
 #include <connman/rtnl.h>
 #include <connman/log.h>
 #include <connman/setting.h>
+#if defined TIZEN_EXT_WIFI_MESH
+#include <connman/mesh.h>
+#endif
 
 static bool eth_tethering = false;
 
@@ -252,6 +255,10 @@ static void ethernet_newlink(unsigned flags, unsigned change, void *user_data)
                } else {
                        DBG("carrier off");
                        remove_network(device, ethernet);
+#if defined TIZEN_EXT_WIFI_MESH
+                       /* Remove ethernet from mesh bridge */
+                       __connman_mesh_remove_ethernet_from_bridge();
+#endif
                }
        }
 
@@ -430,6 +437,54 @@ static struct connman_technology_driver eth_tech_driver = {
        .set_tethering          = eth_tech_set_tethering,
 };
 
+#if defined TIZEN_EXT_WIFI_MESH
+static int eth_mesh_add_to_bridge(const char *bridge)
+{
+       GList *list;
+       struct ethernet_data *ethernet;
+
+       DBG("Add ethernet to bridge %s", bridge);
+
+       for (list = eth_interface_list; list; list = list->next) {
+               int index = GPOINTER_TO_INT(list->data);
+               struct connman_device *device =
+                       connman_device_find_by_index(index);
+
+               if (device) {
+                       ethernet = connman_device_get_data(device);
+                       if (ethernet)
+                               remove_network(device, ethernet);
+               }
+
+               connman_inet_ifup(index);
+
+               connman_inet_add_to_bridge(index, bridge);
+       }
+
+       return 0;
+}
+
+static int eth_mesh_remove_from_bridge(const char *bridge)
+{
+       GList *list;
+
+       DBG("Remove ethernet from bridge %s", bridge);
+
+       for (list = eth_interface_list; list; list = list->next) {
+               int index = GPOINTER_TO_INT(list->data);
+
+               connman_inet_remove_from_bridge(index, bridge);
+       }
+
+       return 0;
+}
+
+static struct connman_mesh_eth_driver eth_mesh_driver = {
+       .add_to_bridge          = eth_mesh_add_to_bridge,
+       .remove_from_bridge     = eth_mesh_remove_from_bridge,
+};
+#endif
+
 static int ethernet_init(void)
 {
        int err;
@@ -438,6 +493,12 @@ static int ethernet_init(void)
        if (err < 0)
                return err;
 
+#if defined TIZEN_EXT_WIFI_MESH
+       err = connman_mesh_eth_driver_register(&eth_mesh_driver);
+       if (err < 0)
+               return err;
+#endif
+
        err = connman_network_driver_register(&eth_network_driver);
        if (err < 0)
                return err;
@@ -455,6 +516,10 @@ static void ethernet_exit(void)
 {
        connman_technology_driver_unregister(&eth_tech_driver);
 
+#if defined TIZEN_EXT_WIFI_MESH
+       connman_mesh_eth_driver_unregister(&eth_mesh_driver);
+#endif
+
        connman_network_driver_unregister(&eth_network_driver);
 
        connman_device_driver_unregister(&eth_dev_driver);
old mode 100755 (executable)
new mode 100644 (file)
index 6103dc8..8e8017d
@@ -163,6 +163,10 @@ struct wifi_data {
 #endif
        int disconnect_code;
        int assoc_code;
+#if defined TIZEN_EXT_WIFI_MESH
+       bool mesh_interface;
+       struct wifi_mesh_info *mesh_info;
+#endif
 };
 
 #if defined TIZEN_EXT
@@ -347,6 +351,506 @@ static void add_pending_wifi_device(struct wifi_data *wifi)
        pending_wifi_device = g_list_append(pending_wifi_device, wifi);
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+struct wifi_mesh_info {
+       struct wifi_data *wifi;
+       GSupplicantInterface *interface;
+       struct connman_mesh *mesh;
+       char *parent_ifname;
+       char *ifname;
+       char *identifier;
+       int index;
+};
+
+struct mesh_change_peer_status_info {
+       char *peer_address;
+       enum connman_mesh_peer_status peer_status;
+       mesh_change_peer_status_cb_t callback;
+       void *user_data;
+};
+
+static struct connman_technology_driver mesh_tech_driver = {
+       .name = "mesh",
+       .type = CONNMAN_SERVICE_TYPE_MESH,
+};
+
+static void mesh_interface_create_callback(int result,
+                                          GSupplicantInterface *interface,
+                                          void *user_data)
+{
+       struct wifi_mesh_info *mesh_info = user_data;
+       struct wifi_data *wifi;
+       bool success = false;
+
+       DBG("result %d ifname %s, mesh_info %p", result,
+                               g_supplicant_interface_get_ifname(interface),
+                               mesh_info);
+
+       if (result < 0 || !mesh_info)
+               goto done;
+
+       wifi = mesh_info->wifi;
+
+       mesh_info->interface = interface;
+       mesh_info->identifier = connman_inet_ifaddr(mesh_info->ifname);
+       mesh_info->index = connman_inet_ifindex(mesh_info->ifname);
+       DBG("Mesh Interface identifier %s", mesh_info->identifier);
+       wifi->mesh_interface = true;
+       wifi->mesh_info = mesh_info;
+       g_supplicant_interface_set_data(interface, wifi);
+       success = true;
+
+done:
+       connman_mesh_notify_interface_create(success);
+}
+
+static int add_mesh_interface(const char *ifname, const char *parent_ifname)
+{
+       GList *list;
+       struct wifi_data *wifi;
+       struct wifi_mesh_info *mesh_info;
+       const char *wifi_ifname;
+       bool parent_found = false;
+       const char *driver = "nl80211";
+
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
+
+               if (!g_supplicant_interface_has_mesh(wifi->interface))
+                       continue;
+
+               wifi_ifname = g_supplicant_interface_get_ifname(wifi->interface);
+               if (!wifi_ifname)
+                       continue;
+
+               if (!g_strcmp0(wifi_ifname, parent_ifname)) {
+                       parent_found = true;
+                       break;
+               }
+       }
+
+       if (!parent_found) {
+               DBG("Parent interface %s doesn't exist", parent_ifname);
+               return -ENODEV;
+       }
+
+       mesh_info = g_try_malloc0(sizeof(struct wifi_mesh_info));
+       if (!mesh_info)
+               return -ENOMEM;
+
+       mesh_info->wifi = wifi;
+       mesh_info->ifname = g_strdup(ifname);
+       mesh_info->parent_ifname = g_strdup(parent_ifname);
+
+       g_supplicant_mesh_interface_create(ifname, driver, NULL, parent_ifname,
+                                               mesh_interface_create_callback, mesh_info);
+       return -EINPROGRESS;
+}
+
+static void mesh_interface_remove_callback(int result,
+                                       GSupplicantInterface *interface,
+                                                       void *user_data)
+{
+       struct wifi_data *wifi = user_data;
+       struct wifi_mesh_info *mesh_info = wifi->mesh_info;
+       bool success = false;
+
+       DBG("result %d mesh_info %p", result, mesh_info);
+
+       if (result < 0 || !mesh_info)
+               goto done;
+
+       mesh_info->interface = NULL;
+       g_free(mesh_info->parent_ifname);
+       g_free(mesh_info->ifname);
+       g_free(mesh_info->identifier);
+       g_free(mesh_info);
+       wifi->mesh_interface = false;
+       wifi->mesh_info = NULL;
+       success = true;
+
+done:
+       connman_mesh_notify_interface_remove(success);
+}
+
+static int remove_mesh_interface(const char *ifname)
+{
+       GList *list;
+       struct wifi_data *wifi;
+       struct wifi_mesh_info *mesh_info;
+       bool mesh_if_found = false;
+       int ret;
+
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
+
+               if (wifi->mesh_interface) {
+                       mesh_if_found = true;
+                       break;
+               }
+       }
+
+       if (!mesh_if_found) {
+               DBG("Mesh interface %s doesn't exist", ifname);
+               return -ENODEV;
+       }
+
+       mesh_info = wifi->mesh_info;
+       ret = g_supplicant_interface_remove(mesh_info->interface,
+                                               mesh_interface_remove_callback, wifi);
+       if (ret < 0)
+               return ret;
+
+       return -EINPROGRESS;
+}
+
+static void mesh_disconnect_callback(int result,
+                                       GSupplicantInterface *interface, void *user_data)
+{
+       struct connman_mesh *mesh = user_data;
+
+       DBG("result %d interface %p mesh %p", result, interface, mesh);
+}
+
+static int mesh_peer_disconnect(struct connman_mesh *mesh)
+{
+       GList *list;
+       struct wifi_data *wifi;
+       struct wifi_mesh_info *mesh_info;
+       bool mesh_if_found = false;
+       GSupplicantInterface *interface;
+
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
+
+               if (wifi->mesh_interface) {
+                       mesh_if_found = true;
+                       break;
+               }
+       }
+
+       if (!mesh_if_found) {
+               DBG("Mesh interface is not created");
+               return -ENODEV;
+       }
+
+       mesh_info = wifi->mesh_info;
+
+       interface = mesh_info->interface;
+       return g_supplicant_interface_disconnect(interface,
+                                               mesh_disconnect_callback, mesh);
+}
+
+static void mesh_connect_callback(int result, GSupplicantInterface *interface,
+                                                                 void *user_data)
+{
+       struct connman_mesh *mesh = user_data;
+       DBG("mesh %p result %d", mesh, result);
+
+       if (result < 0)
+               connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_FAILURE);
+       else
+               connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_ASSOCIATION);
+}
+
+static GSupplicantSecurity mesh_network_security(const char *security)
+{
+       if (g_str_equal(security, "none"))
+               return G_SUPPLICANT_SECURITY_NONE;
+       else if (g_str_equal(security, "sae"))
+               return G_SUPPLICANT_SECURITY_SAE;
+
+       return G_SUPPLICANT_SECURITY_UNKNOWN;
+}
+
+static void mesh_ssid_init(GSupplicantSSID *ssid, struct connman_mesh *mesh)
+{
+       const char *name;
+       const char *security;
+
+       if (ssid->ssid)
+               g_free(ssid->ssid);
+
+       memset(ssid, 0, sizeof(*ssid));
+       ssid->mode = G_SUPPLICANT_MODE_MESH;
+
+       security = connman_mesh_get_security(mesh);
+       ssid->security = mesh_network_security(security);
+
+       if (ssid->security == G_SUPPLICANT_SECURITY_SAE)
+               ssid->passphrase = connman_mesh_get_passphrase(mesh);
+
+       ssid->freq = connman_mesh_get_frequency(mesh);
+       name = connman_mesh_get_name(mesh);
+       if (name) {
+               ssid->ssid_len = strlen(name);
+               ssid->ssid = g_malloc0(ssid->ssid_len + 1);
+               memcpy(ssid->ssid, name, ssid->ssid_len);
+               ssid->scan_ssid = 1;
+       }
+}
+
+static int mesh_peer_connect(struct connman_mesh *mesh)
+{
+       GList *list;
+       struct wifi_data *wifi;
+       struct wifi_mesh_info *mesh_info;
+       bool mesh_if_found = false;
+       GSupplicantInterface *interface;
+       GSupplicantSSID *ssid;
+
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
+
+               if (wifi->mesh_interface) {
+                       mesh_if_found = true;
+                       break;
+               }
+       }
+
+       if (!mesh_if_found) {
+               DBG("Mesh interface is not created");
+               return -ENODEV;
+       }
+
+       mesh_info = wifi->mesh_info;
+
+       interface = mesh_info->interface;
+
+       ssid = g_try_malloc0(sizeof(GSupplicantSSID));
+       if (!ssid)
+               return -ENOMEM;
+
+       mesh_info->mesh = mesh;
+
+       mesh_ssid_init(ssid, mesh);
+       return g_supplicant_interface_connect(interface, ssid,
+                                               mesh_connect_callback, mesh);
+}
+
+static void mesh_peer_change_status_callback(int result,
+                                            GSupplicantInterface *interface,
+                                            void *user_data)
+{
+       struct mesh_change_peer_status_info *data = user_data;
+
+       DBG("result %d Peer Status %d", result, data->peer_status);
+
+       if (result == 0 && data->peer_status == CONNMAN_MESH_PEER_REMOVE) {
+               /* WLAN_REASON_MESH_PEERING_CANCELLED = 52 */
+               connman_mesh_remove_connected_peer(data->peer_address, 52);
+       }
+
+       if (data->callback)
+               data->callback(result, data->user_data);
+
+       g_free(data->peer_address);
+       g_free(data);
+       return;
+}
+
+static int mesh_change_peer_status(const char *peer_address,
+                                  enum connman_mesh_peer_status status,
+                                  mesh_change_peer_status_cb_t callback, void *user_data)
+{
+       GList *list;
+       struct wifi_data *wifi;
+       struct wifi_mesh_info *mesh_info;
+       bool mesh_if_found = false;
+       GSupplicantInterface *interface;
+       struct mesh_change_peer_status_info *data;
+       const char *method;
+
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
+
+               if (wifi->mesh_interface) {
+                       mesh_if_found = true;
+                       break;
+               }
+       }
+
+       if (!mesh_if_found) {
+               DBG("Mesh interface is not created");
+               return -ENODEV;
+       }
+
+       mesh_info = wifi->mesh_info;
+
+       interface = mesh_info->interface;
+
+       switch (status) {
+       case CONNMAN_MESH_PEER_ADD:
+               method = "MeshPeerAdd";
+               break;
+       case CONNMAN_MESH_PEER_REMOVE:
+               method = "MeshPeerRemove";
+               break;
+       default:
+               DBG("Invalid method");
+               return -EINVAL;
+       }
+
+       data = g_try_malloc0(sizeof(struct mesh_change_peer_status_info));
+       if (data == NULL) {
+               DBG("Memory allocation failed");
+               return -ENOMEM;
+       }
+
+       data->peer_address = g_strdup(peer_address);
+       data->peer_status = status;
+       data->callback = callback;
+       data->user_data = user_data;
+
+       return g_supplicant_interface_mesh_peer_change_status(interface,
+                                               mesh_peer_change_status_callback, peer_address, method,
+                                               data);
+}
+
+static struct connman_mesh_driver mesh_driver = {
+       .add_interface      = add_mesh_interface,
+       .remove_interface   = remove_mesh_interface,
+       .connect            = mesh_peer_connect,
+       .disconnect         = mesh_peer_disconnect,
+       .change_peer_status = mesh_change_peer_status,
+};
+
+static void mesh_support(GSupplicantInterface *interface)
+{
+       DBG("");
+
+       if (!g_supplicant_interface_has_mesh(interface))
+               return;
+
+       if (connman_technology_driver_register(&mesh_tech_driver) < 0) {
+               DBG("Could not register Mesh technology driver");
+               return;
+       }
+
+       connman_mesh_driver_register(&mesh_driver);
+}
+
+static void check_mesh_technology(void)
+{
+       bool mesh_exists = false;
+       GList *list;
+
+       for (list = iface_list; list; list = list->next) {
+               struct wifi_data *w = list->data;
+
+               if (w->interface &&
+                               g_supplicant_interface_has_mesh(w->interface))
+                       mesh_exists = true;
+       }
+
+       if (!mesh_exists) {
+               connman_technology_driver_unregister(&mesh_tech_driver);
+               connman_mesh_driver_unregister(&mesh_driver);
+       }
+}
+
+static void mesh_group_started(GSupplicantInterface *interface)
+{
+       struct wifi_data *wifi;
+       struct wifi_mesh_info *mesh_info;
+       struct connman_mesh *mesh;
+       const unsigned char *ssid;
+       unsigned int ssid_len;
+       char name[33];
+
+       ssid = g_supplicant_interface_get_mesh_group_ssid(interface, &ssid_len);
+       memcpy(name, ssid, ssid_len);
+       name[ssid_len] = '\0';
+       DBG("name %s", name);
+       wifi = g_supplicant_interface_get_data(interface);
+       DBG("wifi %p", wifi);
+
+       if (!wifi)
+               return;
+
+       mesh_info = wifi->mesh_info;
+       if (!mesh_info)
+               return;
+
+       mesh = mesh_info->mesh;
+       if (!mesh)
+               return;
+
+       connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_CONFIGURATION);
+}
+
+static void mesh_group_removed(GSupplicantInterface *interface)
+{
+       struct wifi_data *wifi;
+       struct wifi_mesh_info *mesh_info;
+       struct connman_mesh *mesh;
+       const unsigned char *ssid;
+       unsigned int ssid_len;
+       int disconnect_reason;
+       char name[33];
+
+       ssid = g_supplicant_interface_get_mesh_group_ssid(interface, &ssid_len);
+       memcpy(name, ssid, ssid_len);
+       name[ssid_len] = '\0';
+       DBG("name %s", name);
+
+       disconnect_reason = g_supplicant_mesh_get_disconnect_reason(interface);
+       DBG("Disconnect Reason %d", disconnect_reason);
+
+       wifi = g_supplicant_interface_get_data(interface);
+       DBG("wifi %p", wifi);
+
+       if (!wifi)
+               return;
+
+       mesh_info = wifi->mesh_info;
+       if (!mesh_info)
+               return;
+
+       mesh = connman_get_connected_mesh_from_name(name);
+       if (!mesh) {
+               DBG("%s is not connected", name);
+               mesh = connman_get_connecting_mesh_from_name(name);
+               if (!mesh) {
+                       DBG("%s is not connecting", name);
+                       return;
+               }
+       }
+
+       connman_mesh_peer_set_disconnect_reason(mesh, disconnect_reason);
+       connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_DISCONNECT);
+}
+
+static void mesh_peer_connected(GSupplicantMeshPeer *mesh_peer)
+{
+       const char *peer_address;
+
+       peer_address = g_supplicant_mesh_peer_get_address(mesh_peer);
+
+       if (!peer_address)
+               return;
+
+       DBG("Peer %s connected", peer_address);
+       connman_mesh_add_connected_peer(peer_address);
+}
+
+static void mesh_peer_disconnected(GSupplicantMeshPeer *mesh_peer)
+{
+       const char *peer_address;
+       int reason;
+
+       peer_address = g_supplicant_mesh_peer_get_address(mesh_peer);
+
+       if (!peer_address)
+               return;
+
+       reason = g_supplicant_mesh_peer_get_disconnect_reason(mesh_peer);
+
+       DBG("Peer %s disconnected with reason %d", peer_address, reason);
+       connman_mesh_remove_connected_peer(peer_address, reason);
+}
+#endif
+
 static struct wifi_data *get_pending_wifi_data(const char *ifname)
 {
        GList *list;
@@ -1034,6 +1538,9 @@ static void wifi_remove(struct connman_device *device)
                iface_list = g_list_remove(iface_list, wifi);
 
        check_p2p_technology();
+#if defined TIZEN_EXT_WIFI_MESH
+       check_mesh_technology();
+#endif
 
        remove_pending_wifi_device(wifi);
 
@@ -2094,7 +2601,7 @@ static int wifi_specific_scan(enum connman_service_type type,
                        }
 
                        memcpy(scan_ssid->ssid, ssid, (ssid_len + 1));
-                       DBG("scan ssid %s len: %d", scan_ssid->ssid, ssid_len);
+                       /* DBG("scan ssid %s len: %d", scan_ssid->ssid, ssid_len); */
                        scan_ssid->ssid_len = ssid_len;
                        scan_params->ssids = g_slist_prepend(scan_params->ssids, scan_ssid);
                        count++;
@@ -2160,7 +2667,7 @@ static int wifi_specific_scan(enum connman_service_type type,
                                }
 
                                memcpy(scan_ssid->ssid, ssid, (ssid_len + 1));
-                               DBG("scan ssid %s len: %d", scan_ssid->ssid, ssid_len);
+                               /* DBG("scan ssid %s len: %d", scan_ssid->ssid, ssid_len); */
                                scan_ssid->ssid_len = ssid_len;
                                scan_params->ssids = g_slist_prepend(scan_params->ssids, scan_ssid);
                                ap_count++;
@@ -2197,6 +2704,159 @@ static int wifi_specific_scan(enum connman_service_type type,
 }
 #endif
 
+#if defined TIZEN_EXT_WIFI_MESH
+static void mesh_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 scanning;
+
+       DBG("result %d wifi %p", result, wifi);
+
+       scanning = connman_device_get_scanning(device);
+       if (scanning)
+               connman_device_set_scanning(device,
+                               CONNMAN_SERVICE_TYPE_MESH, false);
+
+       if (scanning)
+               connman_device_unref(device);
+}
+
+static int mesh_scan(struct connman_device *device)
+{
+       struct wifi_data *wifi;
+       struct wifi_mesh_info *mesh_info;
+       int ret;
+
+       DBG("");
+
+       wifi = connman_device_get_data(device);
+
+       if (!wifi->mesh_interface)
+               return -ENOTSUP;
+
+       mesh_info = wifi->mesh_info;
+       reset_autoscan(device);
+       connman_device_ref(device);
+
+       ret = g_supplicant_interface_scan(mesh_info->interface, NULL,
+                                               mesh_scan_callback, device);
+       if (ret)
+               connman_device_unref(device);
+       else
+               connman_device_set_scanning(device,
+                               CONNMAN_SERVICE_TYPE_MESH, true);
+
+       return ret;
+}
+
+static void abort_scan_callback(int result, GSupplicantInterface *interface,
+                                               void *user_data)
+{
+       struct connman_device *device = user_data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+
+       DBG("result %d wifi %p", result, wifi);
+
+       __connman_technology_notify_abort_scan(CONNMAN_SERVICE_TYPE_MESH, result);
+}
+
+static int mesh_abort_scan(enum connman_service_type type,
+                                               struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       struct wifi_mesh_info *mesh_info;
+       bool scanning;
+       int ret;
+
+       if (!wifi || !wifi->mesh_interface)
+               return -ENODEV;
+
+       if (type != CONNMAN_SERVICE_TYPE_MESH)
+               return -EINVAL;
+
+       mesh_info = wifi->mesh_info;
+
+       scanning = connman_device_get_scanning(device);
+       if (!scanning)
+               return -EEXIST;
+
+       ret = g_supplicant_interface_abort_scan(mesh_info->interface,
+                                               abort_scan_callback, device);
+
+       return ret;
+}
+
+static int mesh_specific_scan(enum connman_service_type type,
+                             struct connman_device *device, const char *ssid,
+                             unsigned int freq, void *user_data)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       GSupplicantScanParams *scan_params = NULL;
+       struct wifi_mesh_info *mesh_info;
+       struct scan_ssid *scan_ssid;
+       bool scanning;
+       int ret;
+
+       if (!wifi || !wifi->mesh_interface)
+               return -ENODEV;
+
+       if (type != CONNMAN_SERVICE_TYPE_MESH)
+               return -EINVAL;
+
+       if (wifi->p2p_device)
+               return 0;
+
+       mesh_info = wifi->mesh_info;
+
+       scanning = connman_device_get_scanning(device);
+       if (scanning)
+               return -EALREADY;
+
+       scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
+       if (!scan_params)
+               return -ENOMEM;
+
+       scan_ssid = g_try_new(struct scan_ssid, 1);
+       if (!scan_ssid) {
+               g_free(scan_params);
+               return -ENOMEM;
+       }
+
+       scan_ssid->ssid_len = strlen(ssid);
+       memcpy(scan_ssid->ssid, ssid, scan_ssid->ssid_len);
+       scan_params->ssids = g_slist_prepend(scan_params->ssids, scan_ssid);
+       scan_params->num_ssids = 1;
+
+       scan_params->freqs = g_try_new(uint16_t, 1);
+       if (!scan_params->freqs) {
+               g_slist_free_full(scan_params->ssids, g_free);
+               g_free(scan_params);
+               return -ENOMEM;
+       }
+
+       scan_params->freqs[0] = freq;
+       scan_params->num_freqs = 1;
+
+       reset_autoscan(device);
+       connman_device_ref(device);
+
+       ret = g_supplicant_interface_scan(mesh_info->interface, scan_params,
+                                               mesh_scan_callback, device);
+
+       if (ret == 0) {
+               connman_device_set_scanning(device,
+                               CONNMAN_SERVICE_TYPE_MESH, true);
+       } else {
+               g_supplicant_free_scan_params(scan_params);
+               connman_device_unref(device);
+       }
+
+       return ret;
+}
+#endif
+
 /*
  * Note that the hidden scan is only used when connecting to this specific
  * hidden AP first time. It is not used when system autoconnects to hidden AP.
@@ -2228,6 +2888,11 @@ static int wifi_scan(enum connman_service_type type,
        if (type == CONNMAN_SERVICE_TYPE_P2P)
                return p2p_find(device);
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (type == CONNMAN_SERVICE_TYPE_MESH)
+               return mesh_scan(device);
+#endif
+
        DBG("device %p wifi %p hidden ssid %s", device, wifi->interface, ssid);
 
        scanning = connman_device_get_scanning(device);
@@ -2379,6 +3044,10 @@ static struct connman_device_driver wifi_ng_driver = {
 #if defined TIZEN_EXT
        .specific_scan  = wifi_specific_scan,
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+       .abort_scan     = mesh_abort_scan,
+       .mesh_specific_scan     = mesh_specific_scan,
+#endif
 };
 
 static void system_ready(void)
@@ -3238,6 +3907,21 @@ static void interface_removed(GSupplicantInterface *interface)
 
        wifi = g_supplicant_interface_get_data(interface);
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (wifi && wifi->mesh_interface) {
+               DBG("Notify mesh interface remove");
+               connman_mesh_notify_interface_remove(true);
+               struct wifi_mesh_info *mesh_info = wifi->mesh_info;
+               g_free(mesh_info->parent_ifname);
+               g_free(mesh_info->ifname);
+               g_free(mesh_info->identifier);
+               g_free(mesh_info);
+               wifi->mesh_interface = false;
+               wifi->mesh_info = NULL;
+               return;
+       }
+#endif
+
        if (wifi)
                wifi->interface = NULL;
 
@@ -3252,6 +3936,9 @@ static void interface_removed(GSupplicantInterface *interface)
        connman_device_set_powered(wifi->device, false);
 
        check_p2p_technology();
+#if defined TIZEN_EXT_WIFI_MESH
+       check_mesh_technology();
+#endif
 }
 
 static void set_device_type(const char *type, char dev_type[17])
@@ -3391,6 +4078,97 @@ static unsigned char calculate_strength(GSupplicantNetwork *supplicant_network)
        return strength;
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+static void mesh_peer_added(GSupplicantNetwork *supplicant_network)
+{
+       GSupplicantInterface *interface;
+       struct wifi_data *wifi;
+       const char *name, *security;
+       struct connman_mesh *connman_mesh;
+       struct wifi_mesh_info *mesh_info;
+       const unsigned char *bssid;
+       const char *identifier;
+       char *address;
+       uint16_t frequency;
+       int ret;
+
+       interface = g_supplicant_network_get_interface(supplicant_network);
+       wifi = g_supplicant_interface_get_data(interface);
+       if (!wifi || !wifi->mesh_interface) {
+               DBG("Virtual Mesh interface not created");
+               return;
+       }
+
+       bssid = g_supplicant_network_get_bssid(supplicant_network);
+       address = g_malloc0(19);
+       snprintf(address, 19, "%02x:%02x:%02x:%02x:%02x:%02x", bssid[0], bssid[1],
+                                                                bssid[2], bssid[3], bssid[4], bssid[5]);
+
+       identifier = g_supplicant_network_get_identifier(supplicant_network);
+       name = g_supplicant_network_get_name(supplicant_network);
+       security = g_supplicant_network_get_security(supplicant_network);
+       frequency = g_supplicant_network_get_frequency(supplicant_network);
+
+       mesh_info = wifi->mesh_info;
+       connman_mesh = connman_mesh_get(mesh_info->identifier, identifier);
+       if (connman_mesh)
+               goto done;
+
+       DBG("Mesh Peer name %s identifier %s security %s added", name, identifier,
+                                       security);
+       connman_mesh = connman_mesh_create(mesh_info->identifier, identifier);
+       connman_mesh_set_name(connman_mesh, name);
+       connman_mesh_set_security(connman_mesh, security);
+       connman_mesh_set_frequency(connman_mesh, frequency);
+       connman_mesh_set_address(connman_mesh, address);
+       connman_mesh_set_index(connman_mesh, mesh_info->index);
+       connman_mesh_set_strength(connman_mesh,
+                                               calculate_strength(supplicant_network));
+       connman_mesh_set_peer_type(connman_mesh, CONNMAN_MESH_PEER_TYPE_DISCOVERED);
+
+       ret = connman_mesh_register(connman_mesh);
+       if (ret == -EALREADY)
+               DBG("Mesh Peer is already registered");
+
+done:
+       g_free(address);
+}
+
+static void mesh_peer_removed(GSupplicantNetwork *supplicant_network)
+{
+       GSupplicantInterface *interface;
+       struct wifi_data *wifi;
+       struct connman_mesh *connman_mesh;
+       struct wifi_mesh_info *mesh_info;
+       const char *identifier;
+
+       interface = g_supplicant_network_get_interface(supplicant_network);
+       wifi = g_supplicant_interface_get_data(interface);
+       if (!wifi || !wifi->mesh_interface) {
+               DBG("Virtual Mesh interface not created");
+               return;
+       }
+
+       identifier = g_supplicant_network_get_identifier(supplicant_network);
+       if (!identifier) {
+               DBG("Failed to get Mesh Peer identifier");
+               return;
+       }
+
+       mesh_info = wifi->mesh_info;
+       connman_mesh = connman_mesh_get(mesh_info->identifier, identifier);
+       if (connman_mesh) {
+               /* Do not unregister connected mesh peer */
+               if (connman_mesh_peer_is_connected_state(connman_mesh)) {
+                       DBG("Mesh Peer %s is connected", identifier);
+                       return;
+               }
+               DBG("Mesh Peer identifier %s removed", identifier);
+               connman_mesh_unregister(connman_mesh);
+       }
+}
+#endif
+
 static void network_added(GSupplicantNetwork *supplicant_network)
 {
        struct connman_network *network;
@@ -3417,6 +4195,13 @@ static void network_added(GSupplicantNetwork *supplicant_network)
        if (!g_strcmp0(mode, "adhoc"))
                return;
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (!g_strcmp0(mode, "mesh")) {
+               mesh_peer_added(supplicant_network);
+               return;
+       }
+#endif
+
        interface = g_supplicant_network_get_interface(supplicant_network);
        wifi = g_supplicant_interface_get_data(interface);
        name = g_supplicant_network_get_name(supplicant_network);
@@ -3543,6 +4328,15 @@ static void network_removed(GSupplicantNetwork *network)
        const char *name, *identifier;
        struct connman_network *connman_network;
 
+#if defined TIZEN_EXT_WIFI_MESH
+       const char *mode;
+       mode = g_supplicant_network_get_mode(network);
+       if (!g_strcmp0(mode, "mesh")) {
+               mesh_peer_removed(network);
+               return;
+       }
+#endif
+
        interface = g_supplicant_network_get_interface(network);
        wifi = g_supplicant_interface_get_data(interface);
        identifier = g_supplicant_network_get_identifier(network);
@@ -4026,6 +4820,13 @@ static const GSupplicantCallbacks callbacks = {
        .debug                  = debug,
        .disconnect_reasoncode  = disconnect_reasoncode,
        .assoc_status_code      = assoc_status_code,
+#if defined TIZEN_EXT_WIFI_MESH
+       .mesh_support           = mesh_support,
+       .mesh_group_started = mesh_group_started,
+       .mesh_group_removed = mesh_group_removed,
+       .mesh_peer_connected = mesh_peer_connected,
+       .mesh_peer_disconnected = mesh_peer_disconnected,
+#endif
 };
 
 
old mode 100755 (executable)
new mode 100644 (file)
index 75cd717..0126711
@@ -1257,6 +1257,9 @@ static int try_provision_service(struct connman_config_service *config,
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
 
                return -ENOENT;
        }
old mode 100755 (executable)
new mode 100644 (file)
index d1069b9..905467e
@@ -57,6 +57,10 @@ 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);
+#if defined TIZEN_EXT_WIFI_MESH
+DBusMessage *__connman_error_invalid_command(DBusMessage *msg);
+DBusMessage *__connman_error_scan_abort_failed(DBusMessage *msg);
+#endif
 
 int __connman_manager_init(void);
 void __connman_manager_cleanup(void);
@@ -920,6 +924,30 @@ int __connman_peer_service_unregister(const char *owner,
                                        const unsigned char *query,
                                        int query_length, int version);
 
+#if defined TIZEN_EXT_WIFI_MESH
+#include <connman/mesh.h>
+
+int __connman_mesh_init(void);
+void __connman_mesh_cleanup(void);
+bool __connman_technology_get_connected(enum connman_service_type type);
+void __connman_technology_mesh_interface_create_finished(
+                                               enum connman_service_type type, bool success,
+                                               const char *error);
+void __connman_technology_mesh_interface_remove_finished(
+                                               enum connman_service_type type, bool success);
+void __connman_mesh_peer_list_struct(DBusMessageIter *array);
+void __connman_mesh_connected_peer_list_struct(DBusMessageIter *array);
+void __connman_mesh_disconnected_peer_list_struct(DBusMessageIter *array);
+int __connman_mesh_dhcp_start(struct connman_ipconfig *ipconfig,
+                       dhcp_cb callback, gpointer user_data);
+int __connman_device_abort_scan(enum connman_service_type type);
+void __connman_technology_notify_abort_scan(enum connman_service_type type,
+                                               int result);
+int __connman_device_request_mesh_specific_scan(enum connman_service_type type,
+                               const char *name, unsigned int freq);
+void __connman_mesh_auto_connect(void);
+#endif /* TIZEN_EXT_WIFI_MESH */
+
 #include <connman/session.h>
 
 typedef void (* service_iterate_cb) (struct connman_service *service,
old mode 100755 (executable)
new mode 100644 (file)
index 3ec8f71..8b77021
@@ -743,6 +743,11 @@ int connman_device_set_scanning(struct connman_device *device,
 
        __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (type == CONNMAN_SERVICE_TYPE_MESH)
+               __connman_mesh_auto_connect();
+#endif
+
        return 0;
 }
 
@@ -1077,6 +1082,9 @@ int __connman_device_request_specific_scan(enum connman_service_type type,
                return -EOPNOTSUPP;
        case CONNMAN_SERVICE_TYPE_WIFI:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        }
 
@@ -1107,6 +1115,100 @@ int __connman_device_request_specific_scan(enum connman_service_type type,
 
        return last_err;
 }
+
+#if defined TIZEN_EXT_WIFI_MESH
+static int device_abort_scan(enum connman_service_type type,
+                               struct connman_device *device)
+{
+       if (!device->driver || !device->driver->scan)
+               return -EOPNOTSUPP;
+
+       if (!device->powered)
+               return -ENOLINK;
+
+       return device->driver->abort_scan(type, device);
+}
+
+int __connman_device_abort_scan(enum connman_service_type type)
+{
+       GSList *list;
+       int err = -EINVAL;
+
+       if (type != CONNMAN_SERVICE_TYPE_MESH)
+               return -EINVAL;
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+               enum connman_service_type service_type =
+                       __connman_device_get_service_type(device);
+
+               if (service_type != CONNMAN_SERVICE_TYPE_UNKNOWN) {
+                       if (type == CONNMAN_SERVICE_TYPE_MESH)
+                               if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
+                                       continue;
+
+                       if (!device->scanning) {
+                               err = -EEXIST;
+                               continue;
+                       }
+
+                       err = device_abort_scan(type, device);
+               }
+       }
+       return err;
+}
+
+static int device_mesh_specific_scan(enum connman_service_type type,
+                               struct connman_device *device, const char *name,
+                               unsigned int freq)
+{
+       if (!device->driver || !device->driver->mesh_specific_scan)
+               return -EOPNOTSUPP;
+
+       if (!device->powered)
+               return -ENOLINK;
+
+       return device->driver->mesh_specific_scan(type, device, name, freq, NULL);
+}
+
+int __connman_device_request_mesh_specific_scan(enum connman_service_type type,
+                                               const char *name,
+                                               unsigned int freq)
+{
+       bool success = false;
+       int last_err = -ENOSYS;
+       GSList *list;
+       int err;
+
+       if (type != CONNMAN_SERVICE_TYPE_MESH)
+               return -EINVAL;
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+               enum connman_service_type service_type =
+                       __connman_device_get_service_type(device);
+
+               if (service_type != CONNMAN_SERVICE_TYPE_UNKNOWN) {
+                       if (type == CONNMAN_SERVICE_TYPE_MESH)
+                               if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
+                                       continue;
+               }
+
+               err = device_mesh_specific_scan(type, device, name, freq);
+               if (err == 0 || err == -EALREADY || err == -EINPROGRESS) {
+                       success = true;
+               } else {
+                       last_err = err;
+                       DBG("device %p err %d", device, err);
+               }
+       }
+
+       if (success)
+               return 0;
+
+       return last_err;
+}
+#endif /* TIZEN_EXT_WIFI_MESH */
 #endif
 
 int __connman_device_request_scan(enum connman_service_type type)
@@ -1128,6 +1230,9 @@ int __connman_device_request_scan(enum connman_service_type type)
                return -EOPNOTSUPP;
        case CONNMAN_SERVICE_TYPE_WIFI:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        }
 
@@ -1140,6 +1245,11 @@ int __connman_device_request_scan(enum connman_service_type type)
                        if (type == CONNMAN_SERVICE_TYPE_P2P) {
                                if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
                                        continue;
+#if defined TIZEN_EXT_WIFI_MESH
+                       } else if (type == CONNMAN_SERVICE_TYPE_MESH) {
+                               if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
+                                       continue;
+#endif
                        } else if (service_type != type)
                                continue;
                }
old mode 100755 (executable)
new mode 100644 (file)
index c428c1d..26a350b
@@ -730,6 +730,41 @@ char *__connman_dhcp_get_server_address(struct connman_ipconfig *ipconfig)
        return g_dhcp_client_get_server_address(dhcp->dhcp_client);
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+int __connman_mesh_dhcp_start(struct connman_ipconfig *ipconfig,
+                       dhcp_cb callback, gpointer user_data)
+{
+       struct connman_dhcp *dhcp;
+       int err;
+
+       DBG("");
+
+       dhcp = g_hash_table_lookup(ipconfig_table, ipconfig);
+       if (!dhcp) {
+
+               dhcp = g_try_new0(struct connman_dhcp, 1);
+               if (!dhcp)
+                       return -ENOMEM;
+
+               dhcp->ipconfig = ipconfig;
+               __connman_ipconfig_ref(ipconfig);
+
+               err = dhcp_initialize(dhcp);
+
+               if (err < 0) {
+                       g_free(dhcp);
+                       return err;
+               }
+
+               g_hash_table_insert(ipconfig_table, ipconfig, dhcp);
+       }
+
+       dhcp->callback = callback;
+       dhcp->user_data = user_data;
+       return g_dhcp_client_start(dhcp->dhcp_client, NULL);
+}
+#endif
+
 int __connman_dhcp_start(struct connman_ipconfig *ipconfig,
                        struct connman_network *network, dhcp_cb callback,
                        gpointer user_data)
old mode 100755 (executable)
new mode 100644 (file)
index 4f24ae2..1a05920
@@ -185,3 +185,17 @@ DBusMessage *__connman_error_invalid_property(DBusMessage *msg)
        return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
                                ".InvalidProperty", "Invalid property");
 }
+
+#if defined TIZEN_EXT_WIFI_MESH
+DBusMessage *__connman_error_invalid_command(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".InvalidCommand", "Invalid Mesh Command");
+}
+
+DBusMessage *__connman_error_scan_abort_failed(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".ScanAbortFailed", "Scan Abort Failed");
+}
+#endif
old mode 100755 (executable)
new mode 100644 (file)
index 93be1e7..25e5372
@@ -190,6 +190,78 @@ done:
        return err;
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+char *connman_inet_ifaddr(const char *name)
+{
+       struct ifreq ifr;
+       struct ether_addr eth;
+       char *str;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return NULL;
+
+       strncpy(ifr.ifr_name, name, IFNAMSIZ-1);
+
+       err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+       close(sk);
+
+       if (err < 0)
+               return NULL;
+
+       str = g_malloc(18);
+       if (!str)
+               return NULL;
+
+       memcpy(&eth, &ifr.ifr_hwaddr.sa_data, sizeof(eth));
+       snprintf(str, 13, "%02x%02x%02x%02x%02x%02x",
+                                               eth.ether_addr_octet[0],
+                                               eth.ether_addr_octet[1],
+                                               eth.ether_addr_octet[2],
+                                               eth.ether_addr_octet[3],
+                                               eth.ether_addr_octet[4],
+                                               eth.ether_addr_octet[5]);
+
+       return str;
+}
+
+char *connman_inet_ifname2addr(const char *name)
+{
+       struct ifreq ifr;
+       struct ether_addr eth;
+       char *str;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return NULL;
+
+       strncpy(ifr.ifr_name, name, IFNAMSIZ-1);
+
+       err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+       close(sk);
+
+       if (err < 0)
+               return NULL;
+
+       str = g_malloc(18);
+       if (!str)
+               return NULL;
+
+       memcpy(&eth, &ifr.ifr_hwaddr.sa_data, sizeof(eth));
+       snprintf(str, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
+                                               eth.ether_addr_octet[0],
+                                               eth.ether_addr_octet[1],
+                                               eth.ether_addr_octet[2],
+                                               eth.ether_addr_octet[3],
+                                               eth.ether_addr_octet[4],
+                                               eth.ether_addr_octet[5]);
+
+       return str;
+}
+#endif
+
 int connman_inet_ifindex(const char *name)
 {
        struct ifreq ifr;
@@ -347,29 +419,14 @@ void connman_inet_update_device_ident(struct connman_device *device)
        case CONNMAN_DEVICE_TYPE_GADGET:
        case CONNMAN_DEVICE_TYPE_WIFI:
                addr = index2addr(index);
-               break;
-       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
-       case CONNMAN_DEVICE_TYPE_CELLULAR:
-       case CONNMAN_DEVICE_TYPE_GPS:
-       case CONNMAN_DEVICE_TYPE_VENDOR:
-               break;
-       }
-
-       switch (type) {
-       case CONNMAN_DEVICE_TYPE_VENDOR:
-       case CONNMAN_DEVICE_TYPE_GPS:
-               break;
-       case CONNMAN_DEVICE_TYPE_ETHERNET:
-       case CONNMAN_DEVICE_TYPE_GADGET:
                ident = index2ident(index, NULL);
                break;
-       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_CELLULAR:
                ident = index2ident(index, NULL);
                break;
        case CONNMAN_DEVICE_TYPE_BLUETOOTH:
-               break;
-       case CONNMAN_DEVICE_TYPE_CELLULAR:
-               ident = index2ident(index, NULL);
+       case CONNMAN_DEVICE_TYPE_GPS:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
                break;
        }
 
old mode 100755 (executable)
new mode 100644 (file)
index 4bc2266..7a6d802
@@ -806,6 +806,9 @@ int main(int argc, char *argv[])
        __connman_service_init();
        __connman_peer_service_init();
        __connman_peer_init();
+#if defined TIZEN_EXT_WIFI_MESH
+       __connman_mesh_init();
+#endif /* TIZEN_EXT_WIFI_MESH */
        __connman_provider_init();
        __connman_network_init();
        __connman_config_init();
@@ -880,6 +883,9 @@ int main(int argc, char *argv[])
        __connman_firewall_cleanup();
        __connman_peer_service_cleanup();
        __connman_peer_cleanup();
+#if defined TIZEN_EXT_WIFI_MESH
+       __connman_mesh_cleanup();
+#endif /* TIZEN_EXT_WIFI_MESH */
        __connman_ippool_cleanup();
        __connman_device_cleanup();
        __connman_network_cleanup();
old mode 100755 (executable)
new mode 100644 (file)
index 2e1367f..bd44fea
@@ -537,6 +537,114 @@ error:
 
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+static void append_mesh_peer_structs(DBusMessageIter *iter, void *user_data)
+{
+       __connman_mesh_peer_list_struct(iter);
+}
+
+static DBusMessage *get_mesh_peers(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       __connman_dbus_append_objpath_dict_array(reply,
+                                       append_mesh_peer_structs, NULL);
+       return reply;
+}
+
+static DBusMessage *get_connected_mesh_peers(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter, array;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                       DBUS_STRUCT_END_CHAR_AS_STRING, &array);
+
+       __connman_mesh_connected_peer_list_struct(&array);
+       dbus_message_iter_close_container(&iter, &array);
+       return reply;
+}
+
+static DBusMessage *get_disconnected_mesh_peers(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter, array;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                       DBUS_STRUCT_END_CHAR_AS_STRING, &array);
+
+       __connman_mesh_disconnected_peer_list_struct(&array);
+       dbus_message_iter_close_container(&iter, &array);
+       return reply;
+}
+
+static DBusMessage *mesh_add_peer(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       const char *addr;
+       int err;
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+                                                       DBUS_TYPE_INVALID);
+
+       DBG("Address %s", addr);
+
+       err = __connman_mesh_change_peer_status(msg, addr, CONNMAN_MESH_PEER_ADD);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return NULL;
+}
+
+static DBusMessage *mesh_remove_peer(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       const char *addr;
+       int err;
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+                                                       DBUS_TYPE_INVALID);
+
+       DBG("Address %s", addr);
+
+       err = __connman_mesh_change_peer_status(msg, addr,
+                                                                               CONNMAN_MESH_PEER_REMOVE);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return NULL;
+}
+#endif
+
 static const GDBusMethodTable manager_methods[] = {
        { GDBUS_METHOD("GetProperties",
                        NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
@@ -596,6 +704,21 @@ static const GDBusMethodTable manager_methods[] = {
        { GDBUS_METHOD("UnregisterPeerService",
                        GDBUS_ARGS({ "specification", "a{sv}" }), NULL,
                        unregister_peer_service) },
+#if defined TIZEN_EXT_WIFI_MESH
+       { GDBUS_METHOD("GetMeshPeers",
+                       NULL, GDBUS_ARGS({ "peers", "a(oa{sv})" }),
+                       get_mesh_peers) },
+       { GDBUS_METHOD("GetConnectedMeshPeers",
+                       NULL, GDBUS_ARGS({ "peers", "a(a{sv})" }),
+                       get_connected_mesh_peers) },
+       { GDBUS_METHOD("GetDisconnectedMeshPeers",
+                       NULL, GDBUS_ARGS({ "peers", "a(a{sv})" }),
+                       get_disconnected_mesh_peers) },
+       { GDBUS_ASYNC_METHOD("MeshAddPeer", GDBUS_ARGS({ "address", "s" }), NULL,
+                                  mesh_add_peer) },
+       { GDBUS_ASYNC_METHOD("MeshRemovePeer", GDBUS_ARGS({ "address", "s" }), NULL,
+                                  mesh_remove_peer) },
+#endif
        { },
 };
 
diff --git a/src/mesh-netlink.c b/src/mesh-netlink.c
new file mode 100644 (file)
index 0000000..22d6925
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *
+ *  Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ *  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "connman.h"
+#include <connman/mesh-netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+#include <netlink/netlink.h>
+
+static int seq_check_cb(struct nl_msg *msg, void *arg)
+{
+       DBG("");
+
+       return NL_OK;
+}
+
+static int finish_cb(struct nl_msg *msg, void *arg)
+{
+       int *ret = arg;
+
+       DBG("");
+
+       *ret = 0;
+
+       return NL_SKIP;
+}
+
+static int ack_cb(struct nl_msg *msg, void *arg)
+{
+       int *ret = arg;
+
+       DBG("");
+
+       *ret = 0;
+
+       return NL_STOP;
+}
+
+static int valid_cb(struct nl_msg *msg, void *arg)
+{
+       DBG("");
+
+       return NL_SKIP;
+}
+
+static int error_cb(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
+{
+       int *ret = arg;
+
+       *ret = err->error;
+
+       DBG("error %d", *ret);
+
+       return NL_STOP;
+}
+
+int __connman_mesh_netlink_set_gate_announce(mesh_nl80211_global *global,
+                                       int mesh_if_index, bool gate_announce, int hwmp_rootmode)
+{
+       struct nl_msg *msg;
+       struct nlattr *container;
+       struct nl_cb *cb;
+       int err = -1;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       cb = nl_cb_clone(global->cb);
+       if (!cb)
+               goto out;
+
+       genlmsg_put(msg, 0, 0, global->id, 0, 0, NL80211_CMD_SET_MESH_CONFIG, 0);
+       nla_put_u32(msg, NL80211_ATTR_IFINDEX, mesh_if_index);
+
+       container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
+
+       nla_put_u8(msg, NL80211_MESHCONF_HWMP_ROOTMODE, hwmp_rootmode);
+
+       nla_put_u8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, gate_announce);
+
+       nla_nest_end(msg, container);
+
+       err = nl_send_auto_complete(global->nl_socket, msg);
+       if (err < 0) {
+               DBG("Failed to send msg");
+               goto out;
+       }
+
+       err = 1;
+
+       nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_cb, &err);
+       nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_cb, &err);
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_cb, &err);
+       nl_cb_err(cb, NL_CB_CUSTOM, error_cb, &err);
+
+       while (err > 0) {
+               int res = nl_recvmsgs(global->nl_socket, cb);
+               if (res < 0)
+                       DBG("nl_recvmsgs failed: %d", res);
+       }
+
+out:
+       nl_cb_put(cb);
+       nlmsg_free(msg);
+       return err;
+}
+
+mesh_nl80211_global *__connman_mesh_nl80211_global_init(void)
+{
+       mesh_nl80211_global *global;
+
+       DBG("");
+
+       global = g_malloc0(sizeof(mesh_nl80211_global));
+
+       global->nl_socket = nl_socket_alloc();
+       if (!global->nl_socket) {
+               DBG("Failed to allocate netlink socket");
+               g_free(global);
+               return NULL;
+       }
+
+       if (genl_connect(global->nl_socket)) {
+               DBG("Failed to connect to generic netlink");
+               nl_socket_free(global->nl_socket);
+               g_free(global);
+               return NULL;
+       }
+
+       nl_socket_set_buffer_size(global->nl_socket, 8192, 8192);
+
+       global->id = genl_ctrl_resolve(global->nl_socket, "nl80211");
+       if (global->id < 0) {
+               DBG("nl80211 generic netlink not found");
+               nl_socket_free(global->nl_socket);
+               g_free(global);
+               return NULL;
+       }
+
+       global->cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (!global->cb) {
+               DBG("Failed to allocate netwlink callbacks");
+               nl_socket_free(global->nl_socket);
+               g_free(global);
+               return NULL;
+       }
+
+       nl_cb_set(global->cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check_cb, NULL);
+
+       return global;
+}
+
+void __connman_mesh_nl80211_global_deinit(mesh_nl80211_global *global)
+{
+       DBG("");
+
+       nl_cb_put(global->cb);
+       nl_socket_free(global->nl_socket);
+       g_free(global);
+}
diff --git a/src/mesh.c b/src/mesh.c
new file mode 100644 (file)
index 0000000..b902934
--- /dev/null
@@ -0,0 +1,1660 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *
+ *  Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ *  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <gdbus.h>
+
+#include <connman/storage.h>
+#include "connman.h"
+#include <sys/types.h>
+#include <dirent.h>
+#include <linux/if_bridge.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <unistd.h>
+#include "mesh-netlink.h"
+
+static DBusConnection *connection;
+
+static GHashTable *mesh_table;
+static GHashTable *connected_peer_table;
+static GHashTable *disconnected_peer_table;
+
+static struct connman_mesh_driver *mesh_driver;
+static struct connman_mesh_eth_driver *mesh_eth_driver;
+
+char *mesh_ifname;
+char *bridge_interface;
+static unsigned int mesh_autoconnect_timeout;
+static bool is_mesh_if_created;
+bool eth_if_bridged;
+mesh_nl80211_global *nl80211_global;
+
+struct connman_mesh {
+       int refcount;
+       char *identifier;
+       char *name;
+       char *path;
+       char *address;
+       char *interface_addr;
+       enum connman_mesh_security security;
+       char *passphrase;
+       enum connman_mesh_state state;
+       enum connman_mesh_peer_type peer_type;
+       enum connman_mesh_peer_disconnect_reason disconnect_reason;
+       uint16_t frequency;
+       uint8_t strength;
+       bool registered;
+       bool favorite;
+       DBusMessage *pending;
+       int index;
+       int br_index;
+       uint16_t ieee80211w;
+       struct connman_ipconfig *ipconfig;
+};
+
+struct connman_mesh_connected_peer {
+       char *peer_address;
+};
+
+struct connman_mesh_disconnected_peer {
+       char *peer_address;
+       enum connman_mesh_peer_disconnect_reason disconnect_reason;
+};
+
+struct connman_mesh_change_peer_data {
+       DBusMessage *pending;
+       char *peer_address;
+       enum connman_mesh_peer_status status;
+};
+
+static void mesh_dhcp_callback(struct connman_ipconfig *ipconfig,
+                               struct connman_network *network, bool success, gpointer data);
+
+static void mesh_free(gpointer data)
+{
+       struct connman_mesh *mesh = data;
+
+       connman_mesh_unregister(mesh);
+
+       g_free(mesh->path);
+
+       if (mesh->state == CONNMAN_MESH_STATE_CONFIGURATION ||
+                       mesh->state == CONNMAN_MESH_STATE_READY)
+               __connman_dhcp_stop(mesh->ipconfig);
+
+       if (mesh->ipconfig) {
+               __connman_ipconfig_set_ops(mesh->ipconfig, NULL);
+               __connman_ipconfig_set_data(mesh->ipconfig, NULL);
+               __connman_ipconfig_unref(mesh->ipconfig);
+               mesh->ipconfig = NULL;
+       }
+       g_free(mesh->identifier);
+       g_free(mesh->name);
+       g_free(mesh->passphrase);
+       g_free(mesh->interface_addr);
+       g_free(mesh->address);
+       g_free(mesh);
+}
+
+static void mesh_connected_peer_free(gpointer data)
+{
+       struct connman_mesh_connected_peer *peer = data;
+
+       g_free(peer->peer_address);
+       g_free(peer);
+}
+
+static void mesh_disconnected_peer_free(gpointer data)
+{
+       struct connman_mesh_disconnected_peer *peer = data;
+
+       g_free(peer->peer_address);
+       g_free(peer);
+}
+
+static void __mesh_load_and_create_network(char *mesh_id)
+{
+       GKeyFile *keyfile;
+       GString *str;
+       struct connman_mesh *connman_mesh;
+       gchar *name, *passphrase, *peer_type;
+       char *identifier, *group, *address;
+       const char *sec_type, *mesh_ifname;
+       int freq, i;
+
+       keyfile = connman_storage_load_service(mesh_id);
+       if (!keyfile) {
+               DBG("Mesh profile doesn't exist");
+               return;
+       }
+
+       peer_type = g_key_file_get_string(keyfile, mesh_id, "PeerType", NULL);
+       if (g_strcmp0(peer_type, "created")) {
+               DBG("Mesh Profile was not created");
+               goto done;
+       }
+
+       name = g_key_file_get_string(keyfile, mesh_id, "Name", NULL);
+       if (!name) {
+               DBG("Failed to get Mesh Profile Name");
+               goto done;
+       }
+
+       passphrase = g_key_file_get_string(keyfile, mesh_id, "Passphrase", NULL);
+       if (passphrase)
+               sec_type = "sae";
+       else
+               sec_type = "none";
+
+       freq = g_key_file_get_integer(keyfile, mesh_id, "Frequency", NULL);
+
+       mesh_ifname = connman_mesh_get_interface_name();
+
+       str = g_string_sized_new((strlen(name) * 2) + 24);
+
+       for (i = 0; name[i]; i++)
+               g_string_append_printf(str, "%02x", name[i]);
+
+       g_string_append_printf(str, "_mesh");
+
+       if (g_strcmp0(sec_type, "none") == 0)
+               g_string_append_printf(str, "_none");
+       else if (g_strcmp0(sec_type, "sae") == 0)
+               g_string_append_printf(str, "_sae");
+
+       group = g_string_free(str, FALSE);
+
+       identifier = connman_inet_ifaddr(mesh_ifname);
+       address = connman_inet_ifname2addr(mesh_ifname);
+
+       connman_mesh = connman_mesh_create(identifier, group);
+       connman_mesh_set_name(connman_mesh, name);
+       connman_mesh_set_address(connman_mesh, address);
+       connman_mesh_set_security(connman_mesh, sec_type);
+       connman_mesh_set_frequency(connman_mesh, freq);
+       connman_mesh_set_index(connman_mesh, connman_inet_ifindex(mesh_ifname));
+       connman_mesh_set_peer_type(connman_mesh, CONNMAN_MESH_PEER_TYPE_CREATED);
+
+       connman_mesh_register(connman_mesh);
+       g_free(group);
+       g_free(identifier);
+       g_free(address);
+done:
+       g_key_file_free(keyfile);
+}
+
+static bool is_connected(struct connman_mesh *mesh)
+{
+       if (mesh->state == CONNMAN_MESH_STATE_READY)
+               return true;
+
+       return false;
+}
+
+static void mesh_peer_dhcp_refresh(gpointer key, gpointer value,
+                                                                  gpointer user_data)
+{
+       struct connman_mesh *mesh = value;
+
+       DBG("mesh %p state %d", mesh, mesh->state);
+
+       if (is_connected(mesh))
+               __connman_mesh_dhcp_start(mesh->ipconfig, mesh_dhcp_callback, mesh);
+}
+
+int connman_inet_set_stp(int stp)
+{
+       int sk, err = 0;
+       struct ifreq ifr;
+       unsigned long args[4];
+
+       if (!bridge_interface)
+               return -EINVAL;
+
+       sk = socket(AF_LOCAL, SOCK_STREAM, 0);
+       if (sk < 0) {
+               err = -errno;
+               goto out;
+       }
+
+       args[0] = BRCTL_SET_BRIDGE_STP_STATE;
+       args[1] = stp;
+       args[2] = args[3] = 0;
+       memset(&ifr, 0, sizeof(struct ifreq));
+       strncpy(ifr.ifr_name, bridge_interface, sizeof(ifr.ifr_name) - 1);
+       ifr.ifr_data = (char *)args;
+
+       if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0)
+               err = -errno;
+
+       close(sk);
+
+out:
+       if (err < 0)
+               DBG("Set STP Failed error %s", strerror(-err));
+
+       return err;
+}
+
+int __connman_mesh_set_stp_gate_announce(bool gate_announce, int hwmp_rootmode,
+                                               int stp)
+{
+       int err;
+
+       if (!mesh_ifname)
+               return -EINVAL;
+
+       err = connman_inet_set_stp(stp);
+       if (err < 0)
+               return err;
+
+       err = __connman_mesh_netlink_set_gate_announce(nl80211_global,
+                                               connman_inet_ifindex(mesh_ifname), gate_announce,
+                                               hwmp_rootmode);
+
+       return err;
+}
+
+void __connman_mesh_add_ethernet_to_bridge(void)
+{
+       if (is_mesh_if_created) {
+               DBG("");
+               mesh_eth_driver->add_to_bridge(bridge_interface);
+               eth_if_bridged = true;
+               g_hash_table_foreach(mesh_table, mesh_peer_dhcp_refresh, NULL);
+               connman_inet_set_stp(1);
+               __connman_mesh_netlink_set_gate_announce(nl80211_global,
+                                                       connman_inet_ifindex(mesh_ifname), true,
+                                                       MESH_HWMP_ROOTMODE_RANN);
+       }
+}
+
+void __connman_mesh_remove_ethernet_from_bridge(void)
+{
+       if (eth_if_bridged) {
+               DBG("");
+               mesh_eth_driver->remove_from_bridge(bridge_interface);
+               eth_if_bridged = false;
+               g_hash_table_foreach(mesh_table, mesh_peer_dhcp_refresh, NULL);
+               connman_inet_set_stp(0);
+               __connman_mesh_netlink_set_gate_announce(nl80211_global,
+                                                       connman_inet_ifindex(mesh_ifname), false,
+                                                       MESH_HWMP_ROOTMODE_NO_ROOT);
+       }
+}
+
+int connman_mesh_notify_interface_create(bool success)
+{
+       int ret;
+       int index;
+       const char *error = NULL;
+       DIR *dir;
+       struct dirent *d;
+
+       if (!success) {
+               error = "Operation Failed";
+               goto done;
+       }
+
+       if (!bridge_interface) {
+               DBG("Don't create bridge interface");
+               goto done;
+       }
+
+       DBG("Creating bridge [%s]", bridge_interface);
+
+       /* Create bridge interface */
+       ret = __connman_bridge_create(bridge_interface);
+       if (0 != ret) {
+               DBG("Failed to create bridge [%s] : [%s]", bridge_interface,
+                               strerror(-ret));
+               error = "Bridge Creation";
+               success = false;
+               goto done;
+       }
+
+       /* Get Mesh Interface Index */
+       index = connman_inet_ifindex(mesh_ifname);
+       if (index < 0) {
+               DBG("Failed to get interface index for %s", mesh_ifname);
+               error = "Operation Failed";
+               success = false;
+               goto done;
+       }
+
+       /* Add mesh interface into bridge */
+       ret = connman_inet_add_to_bridge(index, bridge_interface);
+       if (0 != ret) {
+               DBG("Failed to add interface[%s] into bridge[%s]", mesh_ifname,
+                                       bridge_interface);
+               error = "Add Mesh into bridge";
+               success = false;
+               goto done;
+       }
+
+       if (__connman_technology_get_connected(CONNMAN_SERVICE_TYPE_ETHERNET)) {
+               mesh_eth_driver->add_to_bridge(bridge_interface);
+               eth_if_bridged = true;
+       }
+
+       index = connman_inet_ifindex(bridge_interface);
+       if (index < 0) {
+               DBG("Failed to get interface index for %s", bridge_interface);
+               error = "Operation Failed";
+               success = false;
+               goto done;
+       }
+
+       /* Make bridge interface UP */
+       ret = connman_inet_ifup(index);
+       if (0 != ret) {
+               DBG("Failed to change bridge interface state");
+               error = "Make bridge interface UP";
+               success = false;
+       }
+
+done:
+       if (success) {
+               is_mesh_if_created = true;
+
+               /* Load previously created mesh profiles */
+               dir = opendir(STORAGEDIR);
+               if (!dir) {
+                       DBG("Failed to open %s directory", STORAGEDIR);
+                       __connman_technology_mesh_interface_create_finished(
+                               CONNMAN_SERVICE_TYPE_MESH, success, error);
+                       return 0;
+               }
+
+               while ((d = readdir(dir))) {
+                       if (g_str_has_prefix(d->d_name, "mesh_")) {
+                               DBG("%s is a mesh profile", d->d_name);
+                               __mesh_load_and_create_network(d->d_name);
+                               __connman_mesh_auto_connect();
+                       }
+               }
+
+               closedir(dir);
+
+       } else {
+               if (eth_if_bridged)
+                       mesh_eth_driver->remove_from_bridge(bridge_interface);
+
+               __connman_bridge_disable(bridge_interface);
+
+               __connman_bridge_remove(bridge_interface);
+
+               mesh_driver->remove_interface(mesh_ifname);
+       }
+       __connman_technology_mesh_interface_create_finished(
+                           CONNMAN_SERVICE_TYPE_MESH, success, error);
+       return 0;
+}
+
+int __connman_mesh_add_virtual_interface(const char *ifname,
+                                               const char *parent_ifname, const char *bridge_ifname)
+{
+       int ret;
+
+       if (!ifname || !parent_ifname)
+               return -EINVAL;
+
+       ret = mesh_driver->add_interface(ifname, parent_ifname);
+       if (ret != -EINPROGRESS) {
+               DBG("Failed to add virtual mesh interface");
+               return ret;
+       }
+
+       mesh_ifname = g_strdup(ifname);
+       bridge_interface = g_strdup(bridge_ifname);
+       DBG("Success adding virtual mesh interface");
+       return 0;
+}
+
+int connman_mesh_notify_interface_remove(bool success)
+{
+       struct connman_device *device;
+       int index;
+       if (success) {
+               g_free(mesh_ifname);
+               mesh_ifname = NULL;
+               g_hash_table_remove_all(mesh_table);
+               is_mesh_if_created = false;
+
+               if (eth_if_bridged) {
+                       if (bridge_interface)
+                               mesh_eth_driver->remove_from_bridge(bridge_interface);
+
+                       device = __connman_device_find_device(
+                                                                       CONNMAN_SERVICE_TYPE_ETHERNET);
+                       if (device) {
+                               index = connman_device_get_index(device);
+                               connman_inet_ifup(index);
+                       }
+                       eth_if_bridged = false;
+               }
+
+               if (bridge_interface) {
+                       __connman_bridge_disable(bridge_interface);
+                       if (__connman_bridge_remove(bridge_interface))
+                               DBG("Failed to remove bridge [%s]", bridge_interface);
+
+                       g_free(bridge_interface);
+                       bridge_interface = NULL;
+               }
+       }
+
+       __connman_technology_mesh_interface_remove_finished(
+                                               CONNMAN_SERVICE_TYPE_MESH, success);
+       return 0;
+}
+
+int __connman_mesh_remove_virtual_interface(const char *ifname)
+{
+       int ret;
+       int index;
+
+       if (!ifname)
+               return -EINVAL;
+
+       if (bridge_interface) {
+               index = connman_inet_ifindex(mesh_ifname);
+               if (index < 0) {
+                       DBG("Failed to get interface index for %s", mesh_ifname);
+                       return -EINVAL;
+               }
+
+               ret = connman_inet_remove_from_bridge(index, bridge_interface);
+               if (0 != ret) {
+                       DBG("Failed to remove interface[%s] freom bridge[%s]", mesh_ifname,
+                               bridge_interface);
+                       return -EINVAL;
+               }
+
+               if (eth_if_bridged)
+                       mesh_eth_driver->remove_from_bridge(bridge_interface);
+
+               __connman_bridge_disable(bridge_interface);
+
+               ret = __connman_bridge_remove(bridge_interface);
+               if (0 != ret) {
+                       DBG("Failed to remove bridge [%s]", bridge_interface);
+                       return -EINVAL;
+               }
+
+               g_free(bridge_interface);
+               bridge_interface = NULL;
+       }
+
+       ret = mesh_driver->remove_interface(ifname);
+       if (ret != -EINPROGRESS) {
+               DBG("Failed to remove virtual mesh interface");
+               return ret;
+       }
+
+       DBG("Success removing virtual mesh interface");
+       return 0;
+}
+
+const char *connman_mesh_get_interface_name(void)
+{
+       return mesh_ifname;
+}
+
+bool connman_mesh_is_interface_created(void)
+{
+       DBG("Mesh interface is %screated", is_mesh_if_created ? "" : "not ");
+       return is_mesh_if_created;
+}
+
+struct connman_mesh *connman_mesh_create(const char *interface_addr,
+                                         const char *identifier)
+{
+       struct connman_mesh *mesh;
+
+       mesh = g_malloc0(sizeof(struct connman_mesh));
+       mesh->identifier = g_strdup_printf("mesh_%s_%s", interface_addr,
+                                          identifier);
+       mesh->interface_addr = g_strdup(interface_addr);
+       mesh->state = CONNMAN_MESH_STATE_IDLE;
+
+       mesh->refcount = 1;
+
+       return mesh;
+}
+
+void connman_mesh_set_name(struct connman_mesh *mesh, const char *name)
+{
+       g_free(mesh->name);
+       mesh->name = g_strdup(name);
+}
+
+const char *connman_mesh_get_name(struct connman_mesh *mesh)
+{
+       return mesh->name;
+}
+
+void connman_mesh_set_passphrase(struct connman_mesh *mesh,
+                                 const char *passphrase)
+{
+       g_free(mesh->passphrase);
+       mesh->passphrase = g_strdup(passphrase);
+}
+
+const char *connman_mesh_get_passphrase(struct connman_mesh *mesh)
+{
+       return mesh->passphrase;
+}
+
+void connman_mesh_set_address(struct connman_mesh *mesh, const char *address)
+{
+       g_free(mesh->address);
+       mesh->address = g_strdup(address);
+}
+
+void connman_mesh_set_security(struct connman_mesh *mesh, const char *security)
+{
+       if (!g_strcmp0(security, "none"))
+               mesh->security = CONNMAN_MESH_SECURITY_NONE;
+       else if (!g_strcmp0(security, "sae"))
+               mesh->security = CONNMAN_MESH_SECURITY_SAE;
+       else
+               mesh->security = CONNMAN_MESH_SECURITY_UNKNOWN;
+}
+
+static const char *security2string(enum connman_mesh_security security)
+{
+       switch (security) {
+       case CONNMAN_MESH_SECURITY_UNKNOWN:
+               break;
+       case CONNMAN_MESH_SECURITY_NONE:
+               return "none";
+       case CONNMAN_MESH_SECURITY_SAE:
+               return "sae";
+       }
+
+       return NULL;
+}
+
+const char *connman_mesh_get_security(struct connman_mesh *mesh)
+{
+       return security2string(mesh->security);
+}
+
+void connman_mesh_set_frequency(struct connman_mesh *mesh, uint16_t frequency)
+{
+       mesh->frequency = frequency;
+}
+
+uint16_t connman_mesh_get_frequency(struct connman_mesh *mesh)
+{
+       return mesh->frequency;
+}
+
+void connman_mesh_set_ieee80211w(struct connman_mesh *mesh, uint16_t ieee80211w)
+{
+       mesh->ieee80211w = ieee80211w;
+}
+
+uint16_t connman_mesh_get_ieee80211w(struct connman_mesh *mesh)
+{
+       return mesh->ieee80211w;
+}
+
+void connman_mesh_set_index(struct connman_mesh *mesh, int index)
+{
+       mesh->index = index;
+
+       if (bridge_interface)
+               mesh->br_index = connman_inet_ifindex(bridge_interface);
+}
+
+void connman_mesh_set_strength(struct connman_mesh *mesh, uint8_t strength)
+{
+       mesh->strength = strength;
+}
+
+static const char *peertype2string(enum connman_mesh_peer_type type)
+{
+       switch (type) {
+       case CONNMAN_MESH_PEER_TYPE_CREATED:
+               return "created";
+       case CONNMAN_MESH_PEER_TYPE_DISCOVERED:
+               return "discovered";
+       }
+
+       return NULL;
+}
+
+void connman_mesh_set_peer_type(struct connman_mesh *mesh,
+                                                               enum connman_mesh_peer_type type)
+{
+       mesh->peer_type = type;
+}
+
+static const char *state2string(enum connman_mesh_state state)
+{
+       switch (state) {
+       case CONNMAN_MESH_STATE_UNKNOWN:
+               break;
+       case CONNMAN_MESH_STATE_IDLE:
+               return "idle";
+       case CONNMAN_MESH_STATE_ASSOCIATION:
+               return "association";
+       case CONNMAN_MESH_STATE_CONFIGURATION:
+               return "configuration";
+       case CONNMAN_MESH_STATE_READY:
+               return "ready";
+       case CONNMAN_MESH_STATE_DISCONNECT:
+               return "disconnect";
+       case CONNMAN_MESH_STATE_FAILURE:
+               return "failure";
+       }
+
+       return NULL;
+}
+
+static enum connman_mesh_peer_disconnect_reason convert_to_disconnect_reason(
+                                                                       int reason)
+{
+       switch (reason) {
+       case 3:
+               return CONNMAN_MESH_DEAUTH_LEAVING;
+       case 52:
+               return CONNMAN_MESH_PEERING_CANCELLED;
+       case 53:
+               return CONNMAN_MESH_MAX_PEERS;
+       case 54:
+               return CONNMAN_MESH_CONFIG_POLICY_VIOLATION;
+       case 55:
+               return CONNMAN_MESH_CLOSE_RCVD;
+       case 56:
+               return CONNMAN_MESH_MAX_RETRIES;
+       case 57:
+               return CONNMAN_MESH_CONFIRM_TIMEOUT;
+       case 58:
+               return CONNMAN_MESH_INVALID_GTK;
+       case 59:
+               return CONNMAN_MESH_INCONSISTENT_PARAMS;
+       case 60:
+               return CONNMAN_MESH_INVALID_SECURITY_CAP;
+       }
+
+       return CONNMAN_MESH_REASON_UNKNOWN;
+}
+
+void connman_mesh_peer_set_disconnect_reason(struct connman_mesh *mesh,
+                                               int disconnect_reason)
+{
+       mesh->disconnect_reason = convert_to_disconnect_reason(disconnect_reason);
+}
+
+static bool is_connecting(struct connman_mesh *mesh)
+{
+       if (mesh->state == CONNMAN_MESH_STATE_ASSOCIATION ||
+                       mesh->state == CONNMAN_MESH_STATE_CONFIGURATION)
+               return true;
+
+       return false;
+}
+
+static int mesh_load(struct connman_mesh *mesh)
+{
+       GKeyFile *keyfile;
+       bool favorite;
+       GError *error = NULL;
+       gchar *str;
+
+       keyfile = connman_storage_load_service(mesh->identifier);
+       if (!keyfile) {
+               DBG("Mesh profile is new");
+               return -EIO;
+       }
+
+       favorite = g_key_file_get_boolean(keyfile,
+                               mesh->identifier, "Favorite", &error);
+
+       if (!error)
+               mesh->favorite = favorite;
+
+       g_clear_error(&error);
+
+       str = g_key_file_get_string(keyfile, mesh->identifier, "Passphrase", NULL);
+
+       if (str) {
+               g_free(mesh->passphrase);
+               mesh->passphrase = str;
+       }
+
+       return 0;
+}
+
+static int mesh_save(struct connman_mesh *mesh)
+{
+       GKeyFile *keyfile;
+
+       keyfile = __connman_storage_open_service(mesh->identifier);
+       if (!keyfile)
+               return -EIO;
+
+       g_key_file_set_string(keyfile, mesh->identifier, "Name", mesh->name);
+       g_key_file_set_integer(keyfile, mesh->identifier, "Frequency",
+                                                               mesh->frequency);
+       g_key_file_set_boolean(keyfile, mesh->identifier, "Favorite",
+                                                               mesh->favorite);
+
+       if (mesh->passphrase)
+               g_key_file_set_string(keyfile, mesh->identifier, "Passphrase",
+                                                         mesh->passphrase);
+
+       g_key_file_set_string(keyfile, mesh->identifier, "PeerType",
+                                                               peertype2string(mesh->peer_type));
+
+       __connman_storage_save_service(keyfile, mesh->identifier);
+
+       g_key_file_free(keyfile);
+
+       return 0;
+}
+
+static void reply_pending(struct connman_mesh *mesh, int error)
+{
+       if (!mesh->pending)
+               return;
+
+       connman_dbus_reply_pending(mesh->pending, error, NULL);
+       mesh->pending = NULL;
+}
+
+static void state_changed(struct connman_mesh *mesh)
+{
+       const char *state;
+
+       state = state2string(mesh->state);
+       if (!state)
+               return;
+
+       connman_dbus_property_changed_basic(mesh->path,
+                                        CONNMAN_MESH_INTERFACE, "State",
+                                        DBUS_TYPE_STRING, &state);
+}
+
+static void mesh_dhcp_callback(struct connman_ipconfig *ipconfig,
+                               struct connman_network *network, bool success, gpointer data)
+{
+       struct connman_mesh *mesh = data;
+       int err;
+
+       if (!success)
+               goto error;
+
+       err = __connman_ipconfig_address_add(ipconfig);
+       if (err < 0)
+               goto error;
+
+       return;
+
+error:
+       connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_FAILURE);
+}
+
+static int mesh_start_dhcp_client(struct connman_mesh *mesh)
+{
+       DBG("");
+
+       __connman_ipconfig_enable(mesh->ipconfig);
+
+       return __connman_mesh_dhcp_start(mesh->ipconfig, mesh_dhcp_callback, mesh);
+}
+
+static void mesh_remove_connected_peer(gpointer key, gpointer value,
+                                               gpointer user_data)
+{
+       struct connman_mesh_connected_peer *peer = value;
+
+       DBG("Remove Peer %s", peer->peer_address);
+       g_hash_table_remove(connected_peer_table, key);
+}
+
+static void mesh_remove_disconnected_peer(gpointer key, gpointer value,
+                                               gpointer user_data)
+{
+       struct connman_mesh_disconnected_peer *peer = value;
+
+       DBG("Remove Peer %s", peer->peer_address);
+       g_hash_table_remove(disconnected_peer_table, key);
+}
+
+int connman_mesh_peer_set_state(struct connman_mesh *mesh,
+                                               enum connman_mesh_state new_state)
+{
+       enum connman_mesh_state old_state = mesh->state;
+
+       DBG("mesh peer %s old state %s new state %s", mesh->name,
+                                       state2string(old_state), state2string(new_state));
+
+       if (old_state == new_state)
+               return -EALREADY;
+
+       switch (new_state) {
+       case CONNMAN_MESH_STATE_UNKNOWN:
+               return -EINVAL;
+       case CONNMAN_MESH_STATE_IDLE:
+       case CONNMAN_MESH_STATE_ASSOCIATION:
+               break;
+       case CONNMAN_MESH_STATE_CONFIGURATION:
+               /* Start Link Local IP Address */
+               mesh_start_dhcp_client(mesh);
+               break;
+       case CONNMAN_MESH_STATE_READY:
+               reply_pending(mesh, 0);
+               mesh->favorite = true;
+               __connman_notifier_connect(CONNMAN_SERVICE_TYPE_MESH);
+
+               /* Set Gate Announce option */
+               if (eth_if_bridged) {
+                       connman_inet_set_stp(1);
+                       __connman_mesh_netlink_set_gate_announce(nl80211_global,
+                                                               connman_inet_ifindex(mesh_ifname), true,
+                                                               MESH_HWMP_ROOTMODE_RANN);
+               }
+
+               mesh_save(mesh);
+               break;
+       case CONNMAN_MESH_STATE_DISCONNECT:
+               __connman_dhcp_stop(mesh->ipconfig);
+               g_hash_table_foreach(connected_peer_table, mesh_remove_connected_peer,
+                                                        NULL);
+               g_hash_table_foreach(disconnected_peer_table,
+                                                        mesh_remove_disconnected_peer, NULL);
+               __connman_notifier_disconnect(CONNMAN_SERVICE_TYPE_MESH);
+               break;
+       case CONNMAN_MESH_STATE_FAILURE:
+               reply_pending(mesh, ECONNABORTED);
+               break;
+       }
+
+       mesh->state = new_state;
+       state_changed(mesh);
+
+       return 0;
+}
+
+bool connman_mesh_peer_is_connected_state(struct connman_mesh *mesh)
+{
+       switch (mesh->state) {
+       case CONNMAN_MESH_STATE_UNKNOWN:
+       case CONNMAN_MESH_STATE_IDLE:
+       case CONNMAN_MESH_STATE_ASSOCIATION:
+       case CONNMAN_MESH_STATE_CONFIGURATION:
+       case CONNMAN_MESH_STATE_DISCONNECT:
+       case CONNMAN_MESH_STATE_FAILURE:
+               break;
+       case CONNMAN_MESH_STATE_READY:
+               return true;
+       }
+
+       return false;
+}
+
+struct connman_mesh *connman_get_connected_mesh_from_name(char *name)
+{
+       GList *list, *start;
+
+       list = g_hash_table_get_values(mesh_table);
+       start = list;
+       for (; list; list = list->next) {
+               struct connman_mesh *mesh = list->data;
+
+               if (!g_strcmp0(mesh->name, name) &&
+                                       mesh->state == CONNMAN_MESH_STATE_READY) {
+                       g_list_free(start);
+                       return mesh;
+               }
+       }
+
+       g_list_free(start);
+
+       return NULL;
+}
+
+struct connman_mesh *connman_get_connecting_mesh_from_name(char *name)
+{
+       GList *list, *start;
+
+       list = g_hash_table_get_values(mesh_table);
+       start = list;
+       for (; list; list = list->next) {
+               struct connman_mesh *mesh = list->data;
+
+               if (!g_strcmp0(mesh->name, name) && is_connecting(mesh)) {
+                       g_list_free(start);
+                       return mesh;
+               }
+       }
+
+       g_list_free(start);
+
+       return NULL;
+}
+
+static void mesh_append_ethernet(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_mesh *mesh = user_data;
+
+       if (mesh->ipconfig)
+               __connman_ipconfig_append_ethernet(mesh->ipconfig, iter);
+}
+
+static void mesh_append_ipv4(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_mesh *mesh = user_data;
+
+       if (!is_connected(mesh))
+               return;
+
+       if (mesh->ipconfig)
+               __connman_ipconfig_append_ipv4(mesh->ipconfig, iter);
+}
+
+static void mesh_append_ipv4config(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_mesh *mesh = user_data;
+
+       if (mesh->ipconfig)
+               __connman_ipconfig_append_ipv4config(mesh->ipconfig, iter);
+}
+
+static void append_properties(DBusMessageIter *iter, struct connman_mesh *mesh)
+{
+       const char *state = state2string(mesh->state);
+       const char *security = security2string(mesh->security);
+       const char *peer_type = peertype2string(mesh->peer_type);
+       const char *type = "mesh";
+       DBusMessageIter dict;
+
+       connman_dbus_dict_open(iter, &dict);
+
+       connman_dbus_dict_append_basic(&dict, "Type", DBUS_TYPE_STRING, &type);
+       connman_dbus_dict_append_basic(&dict, "Name",
+                                       DBUS_TYPE_STRING, &mesh->name);
+       connman_dbus_dict_append_basic(&dict, "BSSID",
+                                       DBUS_TYPE_STRING, &mesh->address);
+       connman_dbus_dict_append_basic(&dict, "State", DBUS_TYPE_STRING, &state);
+       if (security)
+               connman_dbus_dict_append_basic(&dict, "Security",
+                                                               DBUS_TYPE_STRING, &security);
+       connman_dbus_dict_append_basic(&dict, "Frequency",
+                                       DBUS_TYPE_UINT16, &mesh->frequency);
+       connman_dbus_dict_append_basic(&dict, "Favorite",
+                                       DBUS_TYPE_BOOLEAN, &mesh->favorite);
+       connman_dbus_dict_append_basic(&dict, "Strength",
+                                       DBUS_TYPE_BYTE, &mesh->strength);
+       connman_dbus_dict_append_basic(&dict, "PeerType",
+                                       DBUS_TYPE_STRING, &peer_type);
+       connman_dbus_dict_append_basic(&dict, "DisconnectReason",
+                                       DBUS_TYPE_INT32, &mesh->disconnect_reason);
+
+       connman_dbus_dict_append_dict(&dict, "Ethernet", mesh_append_ethernet,
+                                       mesh);
+
+       connman_dbus_dict_append_dict(&dict, "IPv4", mesh_append_ipv4, mesh);
+
+       connman_dbus_dict_append_dict(&dict, "IPv4.Configuration",
+                                       mesh_append_ipv4config, mesh);
+
+       connman_dbus_dict_close(iter, &dict);
+}
+
+static void append_mesh_peer_struct(gpointer key, gpointer value,
+                                               gpointer user_data)
+{
+       DBusMessageIter *array = user_data;
+       struct connman_mesh *mesh = value;
+       DBusMessageIter entry;
+
+       DBG("Mesh Peer path %s", mesh->path);
+       dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
+                                                       NULL, &entry);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+                                                       &mesh->path);
+       append_properties(&entry, mesh);
+       dbus_message_iter_close_container(array, &entry);
+}
+
+void __connman_mesh_peer_list_struct(DBusMessageIter *array)
+{
+       g_hash_table_foreach(mesh_table, append_mesh_peer_struct, array);
+}
+
+static DBusMessage *get_mesh_peer_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct connman_mesh *mesh = data;
+       DBusMessageIter dict;
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &dict);
+       append_properties(&dict, mesh);
+
+       return reply;
+}
+
+static void append_mesh_disconnected_peer_struct(gpointer key, gpointer value,
+                                               gpointer user_data)
+{
+       DBusMessageIter *array = user_data;
+       struct connman_mesh_disconnected_peer *peer = value;
+       DBusMessageIter entry;
+       DBusMessageIter dict;
+
+       dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
+                                                       NULL, &entry);
+
+       connman_dbus_dict_open(&entry, &dict);
+
+       connman_dbus_dict_append_basic(&dict, "PeerAddress",
+                                       DBUS_TYPE_STRING, &peer->peer_address);
+
+       connman_dbus_dict_append_basic(&dict, "DisconnectReason",
+                                       DBUS_TYPE_INT32, &peer->disconnect_reason);
+
+       connman_dbus_dict_close(&entry, &dict);
+       dbus_message_iter_close_container(array, &entry);
+}
+
+void __connman_mesh_disconnected_peer_list_struct(DBusMessageIter *array)
+{
+       g_hash_table_foreach(disconnected_peer_table,
+                                               append_mesh_disconnected_peer_struct, array);
+}
+
+static void append_mesh_connected_peer_struct(gpointer key, gpointer value,
+                                               gpointer user_data)
+{
+       DBusMessageIter *array = user_data;
+       struct connman_mesh_connected_peer *peer = value;
+       DBusMessageIter entry;
+       DBusMessageIter dict;
+
+       dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
+                                                       NULL, &entry);
+
+       connman_dbus_dict_open(&entry, &dict);
+
+       connman_dbus_dict_append_basic(&dict, "PeerAddress",
+                                       DBUS_TYPE_STRING, &peer->peer_address);
+
+       connman_dbus_dict_close(&entry, &dict);
+       dbus_message_iter_close_container(array, &entry);
+}
+
+void __connman_mesh_connected_peer_list_struct(DBusMessageIter *array)
+{
+       g_hash_table_foreach(connected_peer_table,
+                                               append_mesh_connected_peer_struct, array);
+}
+
+int connman_mesh_add_connected_peer(const char *peer_address)
+{
+       struct connman_mesh_connected_peer *peer;
+       struct connman_mesh_connected_peer *temp_peer;
+       struct connman_mesh_disconnected_peer *disconn_peer;
+
+       temp_peer = g_hash_table_lookup(connected_peer_table, peer_address);
+
+       if (temp_peer) {
+               DBG("Mesh Peer %s is already connected", peer_address);
+               return 0;
+       }
+
+       peer = g_malloc0(sizeof(struct connman_mesh_connected_peer));
+       peer->peer_address = g_strdup(peer_address);
+       DBG("Peer %s", peer->peer_address);
+
+       g_hash_table_insert(connected_peer_table, peer->peer_address, peer);
+
+       /* Remove from disconnected Peer Table */
+       disconn_peer = g_hash_table_lookup(disconnected_peer_table, peer_address);
+       if (!disconn_peer) {
+               DBG("Peer %s was never disconnected", peer_address);
+               goto done;
+       }
+
+       g_hash_table_remove(disconnected_peer_table, peer_address);
+done:
+       return 0;
+}
+
+int connman_mesh_remove_connected_peer(const char *peer_address, int reason)
+{
+       struct connman_mesh_connected_peer *peer;
+       struct connman_mesh_disconnected_peer *disconn_peer;
+
+       peer = g_hash_table_lookup(connected_peer_table, peer_address);
+
+       if (!peer) {
+               DBG("Peer %s not connected", peer_address);
+               return 0;
+       }
+
+       g_hash_table_remove(connected_peer_table, peer_address);
+
+       /* Add to Disconnected Peer Table */
+       disconn_peer = g_malloc0(sizeof(struct connman_mesh_disconnected_peer));
+       disconn_peer->peer_address = g_strdup(peer_address);
+       disconn_peer->disconnect_reason = convert_to_disconnect_reason(reason);
+
+       g_hash_table_insert(disconnected_peer_table, disconn_peer->peer_address,
+                                               disconn_peer);
+
+       DBG("Mesh Peer %s removed due to reason %d", peer_address, reason);
+       return 0;
+}
+
+static void __mesh_change_peer_status_cb(int result, void *user_data)
+{
+       struct connman_mesh_change_peer_data *data = user_data;
+
+       DBG("Status %d Peer Address %s result %d", data->status, data->peer_address,
+                                                                               result);
+
+       connman_dbus_reply_pending(data->pending, -result, NULL);
+
+       data->pending = NULL;
+       g_free(data->peer_address);
+       g_free(data);
+}
+
+int __connman_mesh_change_peer_status(DBusMessage *msg,
+                                                                         const char *peer_address,
+                                                                         enum connman_mesh_peer_status status)
+{
+       struct connman_mesh_connected_peer *conn_peer;
+       struct connman_mesh_disconnected_peer *disconn_peer;
+       int err = -ENOTSUP;
+       struct connman_mesh_change_peer_data *data;
+
+       switch (status) {
+       case CONNMAN_MESH_PEER_ADD:
+               conn_peer = g_hash_table_lookup(connected_peer_table, peer_address);
+
+               if (conn_peer) {
+                       DBG("Peer %s already connected", peer_address);
+                       return -EEXIST;
+               }
+
+               break;
+
+       case CONNMAN_MESH_PEER_REMOVE:
+               disconn_peer = g_hash_table_lookup(disconnected_peer_table,
+                                                                       peer_address);
+
+               if (disconn_peer) {
+                       DBG("Peer %s already disconnected", peer_address);
+                       return -EEXIST;
+               }
+
+               break;
+
+       default:
+               DBG("Invalid Status type");
+               return err;
+       }
+
+       if (mesh_driver->change_peer_status) {
+               data = g_try_malloc0(sizeof(struct connman_mesh_disconnected_peer));
+               if (data == NULL) {
+                       DBG("Memory allocation failed");
+                       return -ENOMEM;
+               }
+
+               data->pending = dbus_message_ref(msg);
+               data->peer_address = g_strdup(peer_address);
+               data->status = status;
+
+               err = mesh_driver->change_peer_status(peer_address, status,
+                                                               __mesh_change_peer_status_cb, data);
+
+               if (err < 0) {
+                       dbus_message_unref(data->pending);
+                       g_free(data->peer_address);
+                       g_free(data);
+               }
+       }
+
+       return err;
+}
+
+static int mesh_peer_connect(struct connman_mesh *mesh)
+{
+       int err = -ENOTSUP;
+       if (mesh_driver->connect)
+               err = mesh_driver->connect(mesh);
+
+       /* Reset Disconnect Reason */
+       mesh->disconnect_reason = CONNMAN_MESH_REASON_UNKNOWN;
+       return err;
+}
+
+static DBusMessage *connect_mesh_peer(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_mesh *mesh = user_data;
+       int err;
+
+       DBG("mesh %p", mesh);
+
+       if (mesh->state == CONNMAN_MESH_STATE_READY) {
+               DBG("mesh %s already connected", mesh->name);
+               return __connman_error_already_exists(msg);
+       }
+
+       if (mesh->pending)
+               return __connman_error_in_progress(msg);
+
+       mesh->pending = dbus_message_ref(msg);
+
+       err = mesh_peer_connect(mesh);
+       if (err == -EINPROGRESS)
+               return NULL;
+
+       if (err < 0) {
+               dbus_message_unref(mesh->pending);
+               mesh->pending = NULL;
+               return __connman_error_failed(msg, -err);
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void auto_connect_mesh_peer(gpointer key, gpointer value,
+                                               gpointer user_data)
+{
+       bool *conn_started = user_data;
+       struct connman_mesh *mesh = value;
+       int err;
+
+       if (*conn_started)
+               return;
+
+       if (!mesh->favorite || mesh->state != CONNMAN_MESH_STATE_IDLE)
+               return;
+
+       err = mesh_peer_connect(mesh);
+       if (err == -EINPROGRESS)
+               *conn_started = 1;
+}
+
+static gboolean run_mesh_auto_connect(gpointer data)
+{
+       bool conn_started;
+
+       mesh_autoconnect_timeout = 0;
+       DBG("");
+
+       conn_started = false;
+       g_hash_table_foreach(mesh_table, auto_connect_mesh_peer, &conn_started);
+       return FALSE;
+}
+
+void __connman_mesh_auto_connect(void)
+{
+       DBG("");
+
+       if (mesh_autoconnect_timeout != 0)
+               return;
+
+       mesh_autoconnect_timeout = g_idle_add(run_mesh_auto_connect, NULL);
+}
+
+static void mesh_peer_up(struct connman_ipconfig *ipconfig, const char *ifname)
+{
+       DBG("%s up", ifname);
+}
+
+static void mesh_peer_down(struct connman_ipconfig *ipconfig,
+                                               const char *ifname)
+{
+       DBG("%s down", ifname);
+}
+
+static void mesh_peer_lower_up(struct connman_ipconfig *ipconfig,
+                                                       const char *ifname)
+{
+       DBG("%s lower up", ifname);
+}
+
+static void mesh_peer_lower_down(struct connman_ipconfig *ipconfig,
+                                                       const char *ifname)
+{
+       DBG("%s lower down", ifname);
+}
+
+static void mesh_peer_ip_bound(struct connman_ipconfig *ipconfig,
+                                                       const char *ifname)
+{
+       struct connman_mesh *mesh = __connman_ipconfig_get_data(ipconfig);
+
+       DBG("%s ip bound", ifname);
+
+       connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_READY);
+}
+
+static void mesh_peer_ip_release(struct connman_ipconfig *ipconfig,
+                                                       const char *ifname)
+{
+       DBG("%s ip release", ifname);
+}
+
+static const struct connman_ipconfig_ops mesh_peer_ip_ops = {
+       .up                     = mesh_peer_up,
+       .down           = mesh_peer_down,
+       .lower_up       = mesh_peer_lower_up,
+       .lower_down     = mesh_peer_lower_down,
+       .ip_bound       = mesh_peer_ip_bound,
+       .ip_release     = mesh_peer_ip_release,
+       .route_set      = NULL,
+       .route_unset    = NULL,
+};
+
+static struct connman_ipconfig *create_ipconfig(int index, void *user_data)
+{
+       struct connman_ipconfig *ipconfig;
+
+       ipconfig = __connman_ipconfig_create(index,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (!ipconfig)
+               return NULL;
+
+       __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
+       __connman_ipconfig_set_data(ipconfig, user_data);
+       __connman_ipconfig_set_ops(ipconfig, &mesh_peer_ip_ops);
+
+       return ipconfig;
+}
+
+static int __connman_mesh_peer_disconnect(struct connman_mesh *mesh)
+{
+       int err;
+
+       reply_pending(mesh, ECONNABORTED);
+
+       if (!is_connected(mesh) && !is_connecting(mesh))
+               return -ENOTCONN;
+
+       err = mesh_driver->disconnect(mesh);
+       if (err < 0 && err != -EINPROGRESS)
+               return err;
+
+       return err;
+}
+
+static DBusMessage *disconnect_mesh_peer(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_mesh *mesh = user_data;
+       int err;
+
+       DBG("mesh %p", mesh);
+       err = __connman_mesh_peer_disconnect(mesh);
+       if (err < 0 && err != -EINPROGRESS)
+               return __connman_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static bool __connman_mesh_peer_remove(struct connman_mesh *mesh)
+{
+       if (!mesh->favorite)
+               return false;
+
+       __connman_mesh_peer_disconnect(mesh);
+
+       mesh->favorite = false;
+
+       __connman_storage_remove_service(mesh->identifier);
+
+       return true;
+}
+
+static DBusMessage *remove_mesh_peer(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_mesh *mesh = user_data;
+
+       DBG("mesh %p", mesh);
+
+       if (!__connman_mesh_peer_remove(mesh))
+               return __connman_error_not_supported(msg);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *set_mesh_peer_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_mesh *mesh = user_data;
+       DBusMessageIter iter, value;
+       const char *name;
+       int type;
+
+       DBG("mesh %p", mesh);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __connman_error_invalid_arguments(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_recurse(&iter, &value);
+
+       type = dbus_message_iter_get_arg_type(&value);
+
+       if (g_str_equal(name, "Passphrase")) {
+               char *passphrase;
+
+               if (type != DBUS_TYPE_STRING)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &passphrase);
+
+               connman_mesh_set_passphrase(mesh, passphrase);
+       } else {
+               DBG("Invalid Property %s", name);
+               return __connman_error_invalid_property(msg);
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable mesh_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_mesh_peer_properties) },
+       { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, connect_mesh_peer) },
+       { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect_mesh_peer) },
+       { GDBUS_METHOD("Remove", NULL, NULL, remove_mesh_peer) },
+       { GDBUS_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, set_mesh_peer_property) },
+       { },
+};
+
+static const GDBusSignalTable mesh_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { },
+};
+
+int connman_mesh_register(struct connman_mesh *mesh)
+{
+       struct connman_mesh *temp;
+       DBG("mesh %p", mesh);
+
+       if (mesh->path)
+               return -EALREADY;
+
+       mesh->path = g_strdup_printf("%s/mesh/%s", CONNMAN_PATH,
+                                    mesh->identifier);
+       DBG("path %s", mesh->path);
+
+       temp = g_hash_table_lookup(mesh_table, mesh->path);
+       if (temp) {
+               DBG("mesh path %s already exists", mesh->path);
+
+               if (mesh->frequency != temp->frequency) {
+                       DBG("Update frequency for mesh network %s", mesh->name);
+                       connman_mesh_set_frequency(temp, mesh->frequency);
+               }
+
+               mesh_free(mesh);
+               return -EALREADY;
+       }
+
+       if (mesh->br_index > 0)
+               mesh->ipconfig = create_ipconfig(mesh->br_index, mesh);
+       else
+               mesh->ipconfig = create_ipconfig(mesh->index, mesh);
+
+       if (!mesh->ipconfig)
+               return -ENOMEM;
+
+       g_hash_table_insert(mesh_table, mesh->path, mesh);
+
+       mesh_load(mesh);
+
+       g_dbus_register_interface(connection, mesh->path,
+                                       CONNMAN_MESH_INTERFACE,
+                                       mesh_methods, mesh_signals,
+                                       NULL, mesh, NULL);
+       mesh->registered = true;
+       return 0;
+}
+
+void connman_mesh_unregister(struct connman_mesh *mesh)
+{
+       DBG("mesh %p", mesh);
+
+       if (!mesh->path || !mesh->registered)
+               return;
+
+       g_dbus_unregister_interface(connection, mesh->path,
+                                                               CONNMAN_MESH_INTERFACE);
+       mesh->registered = false;
+
+       g_hash_table_remove(mesh_table, mesh->path);
+}
+
+struct connman_mesh *connman_mesh_get(const char *interface_addr,
+                                                                       const char *identifier)
+{
+       char *ident = g_strdup_printf("%s/mesh/mesh_%s_%s", CONNMAN_PATH,
+                                                                       interface_addr, identifier);
+       struct connman_mesh *mesh;
+
+       mesh = g_hash_table_lookup(mesh_table, ident);
+       g_free(ident);
+
+       return mesh;
+}
+
+int connman_mesh_driver_register(struct connman_mesh_driver *driver)
+{
+       if (mesh_driver && mesh_driver != driver)
+               return -EINVAL;
+
+       mesh_driver = driver;
+
+       return 0;
+}
+
+void connman_mesh_driver_unregister(struct connman_mesh_driver *driver)
+{
+       if (mesh_driver != driver)
+               return;
+
+       mesh_driver = NULL;
+}
+
+int connman_mesh_eth_driver_register(struct connman_mesh_eth_driver *driver)
+{
+       if (mesh_eth_driver && mesh_eth_driver != driver)
+               return -EINVAL;
+
+       mesh_eth_driver = driver;
+
+       return 0;
+}
+
+void connman_mesh_eth_driver_unregister(struct connman_mesh_eth_driver *driver)
+{
+       if (mesh_eth_driver != driver)
+               return;
+
+       mesh_eth_driver = NULL;
+}
+
+int __connman_mesh_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+
+       mesh_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       NULL, mesh_free);
+
+       connected_peer_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+                                                       mesh_connected_peer_free);
+
+       disconnected_peer_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       NULL, mesh_disconnected_peer_free);
+
+       nl80211_global = __connman_mesh_nl80211_global_init();
+       return 0;
+}
+
+void __connman_mesh_cleanup(void)
+{
+       DBG("");
+
+       __connman_mesh_nl80211_global_deinit(nl80211_global);
+       g_hash_table_destroy(mesh_table);
+       g_hash_table_destroy(connected_peer_table);
+       g_hash_table_destroy(disconnected_peer_table);
+       dbus_connection_unref(connection);
+}
old mode 100755 (executable)
new mode 100644 (file)
index 5ba5324..7c3d031
@@ -153,6 +153,9 @@ void __connman_notifier_connect(enum connman_service_type type)
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        }
 
@@ -200,6 +203,9 @@ void __connman_notifier_disconnect(enum connman_service_type type)
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        }
 
old mode 100755 (executable)
new mode 100644 (file)
index ac29f31..d733288
@@ -462,6 +462,20 @@ static void process_newlink(unsigned short type, int index, unsigned flags,
        if (!extract_link(msg, bytes, &address, &ifname, &mtu, &operstate, &stats))
                return;
 
+#if defined TIZEN_EXT_WIFI_MESH
+       /* Do not accept Wi-Fi Mesh interface */
+       if (g_strrstr(ifname, "mesh") != NULL) {
+               DBG("Newlink event for Wi-Fi Mesh interface ignored");
+               return;
+       }
+
+       /* Do not accept Wi-Fi WLAN1 interface "dedicated for softAP */
+       if (!g_strcmp0(ifname, "wlan1")) {
+               DBG("Newlink event for Wi-Fi WLAN1 interface ignored");
+               return;
+       }
+#endif
+
 #if defined TIZEN_EXT
        /* Do not accept Wi-Fi P2P interface */
        if (g_strrstr(ifname, "p2p") != NULL) {
old mode 100755 (executable)
new mode 100644 (file)
index d7a7039..d13aafb
@@ -298,6 +298,10 @@ const char *__connman_service_type2string(enum connman_service_type type)
                return "gadget";
        case CONNMAN_SERVICE_TYPE_P2P:
                return "p2p";
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+               return "mesh";
+#endif
        }
 
        return NULL;
@@ -596,6 +600,9 @@ int __connman_service_load_modifiable(struct connman_service *service)
        case CONNMAN_SERVICE_TYPE_SYSTEM:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        case CONNMAN_SERVICE_TYPE_VPN:
                set_split_routing(service, g_key_file_get_boolean(keyfile,
@@ -671,6 +678,9 @@ static int service_load(struct connman_service *service)
        case CONNMAN_SERVICE_TYPE_SYSTEM:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        case CONNMAN_SERVICE_TYPE_VPN:
                set_split_routing(service, g_key_file_get_boolean(keyfile,
@@ -933,6 +943,9 @@ static int service_save(struct connman_service *service)
        case CONNMAN_SERVICE_TYPE_SYSTEM:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        case CONNMAN_SERVICE_TYPE_VPN:
                g_key_file_set_boolean(keyfile, service->identifier,
@@ -3427,6 +3440,9 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        case CONNMAN_SERVICE_TYPE_CELLULAR:
                val = service->roaming;
@@ -5046,6 +5062,9 @@ void __connman_service_set_active_session(bool enable, GSList *list)
                case CONNMAN_SERVICE_TYPE_GPS:
                case CONNMAN_SERVICE_TYPE_VPN:
                case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+               case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                        break;
                }
 
@@ -5181,6 +5200,12 @@ static bool auto_connect_service(GList *services,
 
        ignore[CONNMAN_SERVICE_TYPE_VPN] = true;
 
+#if defined TIZEN_EXT_WIFI_MESH
+       /* Don't auto connect wifi if mesh interface is created */
+       if (connman_mesh_is_interface_created())
+               ignore[CONNMAN_SERVICE_TYPE_WIFI] = true;
+#endif
+
        for (list = services; list; list = list->next) {
                service = list->data;
 
@@ -8060,6 +8085,9 @@ static int service_connect(struct connman_service *service)
        case CONNMAN_SERVICE_TYPE_SYSTEM:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                return -EINVAL;
        case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_GADGET:
@@ -8203,6 +8231,9 @@ int __connman_service_connect(struct connman_service *service,
        case CONNMAN_SERVICE_TYPE_SYSTEM:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                return -EINVAL;
 
        case CONNMAN_SERVICE_TYPE_ETHERNET:
@@ -9025,6 +9056,9 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
        case CONNMAN_SERVICE_TYPE_WIFI:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        case CONNMAN_SERVICE_TYPE_ETHERNET:
                service->favorite = true;
@@ -9056,6 +9090,9 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
                        case CONNMAN_SERVICE_TYPE_UNKNOWN:
                        case CONNMAN_SERVICE_TYPE_SYSTEM:
                        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+                       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                                break;
 
                        case CONNMAN_SERVICE_TYPE_GADGET:
old mode 100755 (executable)
new mode 100644 (file)
index 9e3c559..26cbf87
@@ -194,6 +194,9 @@ static char *service2bearer(enum connman_service_type type)
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_P2P:
        case CONNMAN_SERVICE_TYPE_UNKNOWN:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                return "";
        }
 
old mode 100755 (executable)
new mode 100644 (file)
index 57ab8e1..6604599
@@ -95,6 +95,9 @@ struct connman_technology {
        bool softblocked;
        bool hardblocked;
        bool dbus_registered;
+#if defined TIZEN_EXT_WIFI_MESH
+       DBusMessage *mesh_dbus_msg;
+#endif
 };
 
 static GSList *driver_list = NULL;
@@ -165,6 +168,10 @@ static const char *get_name(enum connman_service_type type)
                return "Cellular";
        case CONNMAN_SERVICE_TYPE_P2P:
                return "P2P";
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+               return "Mesh";
+#endif
        }
 
        return NULL;
@@ -670,6 +677,11 @@ static int technology_affect_devices(struct connman_technology *technology,
                return 0;
        }
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (technology->type == CONNMAN_SERVICE_TYPE_MESH)
+               return 0;
+#endif
+
        for (list = technology->device_list; list; list = list->next) {
                struct connman_device *device = list->data;
 
@@ -1433,6 +1445,471 @@ static DBusMessage *get_scan_state(DBusConnection *conn, DBusMessage *msg, void
 }
 #endif
 
+#if defined TIZEN_EXT_WIFI_MESH
+bool __connman_technology_get_connected(enum connman_service_type type)
+{
+       struct connman_technology *technology;
+
+       technology = technology_find(type);
+
+       if (!technology)
+               return false;
+
+       return technology->connected;
+}
+
+void __connman_technology_mesh_interface_create_finished(
+                                                       enum connman_service_type type, bool success,
+                                                       const char *error)
+{
+       DBusMessage *reply;
+       struct connman_technology *technology;
+       DBusMessage *msg;
+       technology = technology_find(type);
+
+       DBG("technology %p success %d", technology, success);
+
+       if (!technology)
+               return;
+
+       msg = technology->mesh_dbus_msg;
+       if (!msg) {
+               DBG("No pending dbus message");
+               return;
+       }
+
+       if (success) {
+               reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+               __connman_device_request_scan(technology->type);
+       } else
+               reply = g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".MeshInterfaceAddFailed", "%s", error);
+       g_dbus_send_message(connection, reply);
+       dbus_message_unref(msg);
+       technology->mesh_dbus_msg = NULL;
+}
+
+void __connman_technology_mesh_interface_remove_finished(
+                                                       enum connman_service_type type, bool success)
+{
+       DBusMessage *reply;
+       struct connman_technology *technology;
+       DBusMessage *msg;
+       technology = technology_find(type);
+
+       DBG("technology %p success %d", technology, success);
+
+       if (!technology || !technology->mesh_dbus_msg)
+               return;
+
+       msg = technology->mesh_dbus_msg;
+       if (!msg) {
+               DBG("No pending dbus message");
+               return;
+       }
+
+       if (success)
+               reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+       else
+               reply = __connman_error_failed(msg, EINVAL);
+       g_dbus_send_message(connection, reply);
+       dbus_message_unref(msg);
+       technology->mesh_dbus_msg = NULL;
+}
+
+void __connman_technology_notify_abort_scan(enum connman_service_type type,
+                                                       int result)
+{
+       DBusMessage *reply;
+       struct connman_technology *technology;
+       DBusMessage *msg;
+       technology = technology_find(type);
+
+       DBG("technology %p result %d", technology, result);
+
+       if (!technology || !technology->mesh_dbus_msg)
+               return;
+
+       msg = technology->mesh_dbus_msg;
+       if (!msg) {
+               DBG("No pending dbus message");
+               return;
+       }
+
+       if (result < 0)
+               reply = __connman_error_scan_abort_failed(msg);
+       else
+               reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+       g_dbus_send_message(connection, reply);
+       dbus_message_unref(msg);
+       technology->mesh_dbus_msg = NULL;
+}
+
+static DBusMessage *mesh_commands(DBusConnection *conn,
+                                 DBusMessage *msg, void *data)
+{
+       struct connman_technology *technology = data;
+       DBusMessageIter iter, value, dict;
+       const char *cmd = NULL, *ifname = NULL, *parent_ifname = NULL;
+       int err;
+
+       DBG("conn %p", conn);
+
+       if (technology->type != CONNMAN_SERVICE_TYPE_MESH)
+               return __connman_error_invalid_arguments(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __connman_error_invalid_arguments(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &cmd);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
+               return __connman_error_invalid_arguments(msg);
+
+       DBG("Mesh Command %s", cmd);
+       if (g_str_equal(cmd, "MeshInterfaceAdd")) {
+               dbus_message_iter_recurse(&value, &dict);
+               const char *bridge_ifname = NULL;
+               while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+                       DBusMessageIter entry, value2;
+                       const char *key;
+                       int type;
+
+                       dbus_message_iter_recurse(&dict, &entry);
+
+                       if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                               return __connman_error_invalid_arguments(msg);
+
+                       dbus_message_iter_get_basic(&entry, &key);
+                       dbus_message_iter_next(&entry);
+
+                       if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                               return __connman_error_invalid_arguments(msg);
+
+                       dbus_message_iter_recurse(&entry, &value2);
+
+                       type = dbus_message_iter_get_arg_type(&value2);
+
+                       if (g_str_equal(key, "Ifname")) {
+                               if (type != DBUS_TYPE_STRING)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &ifname);
+                       } else if (g_str_equal(key, "ParentIfname")) {
+                               if (type != DBUS_TYPE_STRING)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &parent_ifname);
+                       } else if (g_str_equal(key, "BridgeIfname")) {
+                               if (type != DBUS_TYPE_STRING)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &bridge_ifname);
+                       }
+                       dbus_message_iter_next(&dict);
+               }
+               DBG("Mesh Ifname %s parent %s bridge %s", ifname, parent_ifname,
+                                       bridge_ifname ? bridge_ifname : "NULL");
+               err = __connman_mesh_add_virtual_interface(ifname, parent_ifname,
+                                                          bridge_ifname);
+
+               if (err != 0) {
+                       DBG("Failed to add virtual mesh interface");
+                       return __connman_error_failed(msg, -err);
+               }
+
+               DBG("Successfully added virtual mesh interface");
+
+               dbus_message_ref(msg);
+               technology->mesh_dbus_msg = msg;
+
+       } else if (g_str_equal(cmd, "MeshInterfaceRemove")) {
+               dbus_message_iter_recurse(&value, &dict);
+               while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+                       DBusMessageIter entry, value2;
+                       const char *key;
+                       int type;
+
+                       dbus_message_iter_recurse(&dict, &entry);
+
+                       if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                               return __connman_error_invalid_arguments(msg);
+
+                       dbus_message_iter_get_basic(&entry, &key);
+                       dbus_message_iter_next(&entry);
+
+                       if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                               return __connman_error_invalid_arguments(msg);
+
+                       dbus_message_iter_recurse(&entry, &value2);
+
+                       type = dbus_message_iter_get_arg_type(&value2);
+
+                       if (g_str_equal(key, "Ifname")) {
+                               if (type != DBUS_TYPE_STRING)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &ifname);
+                       }
+                       dbus_message_iter_next(&dict);
+               }
+               DBG("Mesh Ifname %s", ifname);
+               err = __connman_mesh_remove_virtual_interface(ifname);
+
+               if (err != 0) {
+                       DBG("Failed to remove virtual mesh interface");
+                       return __connman_error_failed(msg, -err);
+               }
+
+               DBG("Successfully removed virtual mesh interface");
+
+               dbus_message_ref(msg);
+               technology->mesh_dbus_msg = msg;
+
+       } else if (g_str_equal(cmd, "MeshCreateNetwork")) {
+               struct connman_mesh *connman_mesh;
+               const char *name = NULL;
+               const char *sec_type = NULL;
+               const char *mesh_ifname = NULL;
+               char *identifier, *group, *address;
+               unsigned int freq = 0;
+               unsigned int ieee80211w = 0;
+               GString *str;
+               int i;
+               dbus_message_iter_recurse(&value, &dict);
+               while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+                       DBusMessageIter entry, value2;
+                       const char *key;
+                       int type;
+
+                       dbus_message_iter_recurse(&dict, &entry);
+
+                       if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                               return __connman_error_invalid_arguments(msg);
+
+                       dbus_message_iter_get_basic(&entry, &key);
+                       dbus_message_iter_next(&entry);
+
+                       if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                               return __connman_error_invalid_arguments(msg);
+
+                       dbus_message_iter_recurse(&entry, &value2);
+
+                       type = dbus_message_iter_get_arg_type(&value2);
+
+                       if (g_str_equal(key, "Name")) {
+                               if (type != DBUS_TYPE_STRING)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &name);
+                       } else if (g_str_equal(key, "Frequency")) {
+                               if (type != DBUS_TYPE_UINT16)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &freq);
+                       } else if (g_str_equal(key, "Security")) {
+                               if (type != DBUS_TYPE_STRING)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &sec_type);
+                       } else if (g_str_equal(key, "Pmf")) {
+                               if (type != DBUS_TYPE_UINT16)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &ieee80211w);
+                       }
+                       dbus_message_iter_next(&dict);
+               }
+
+               if (name == NULL || sec_type == NULL || freq == 0)
+                       return __connman_error_invalid_arguments(msg);
+
+               DBG("Name %s Frequency %d Security type %s Pmf %u",
+                   name, freq, sec_type, ieee80211w);
+
+               if (g_strcmp0(sec_type, "none") != 0 &&
+                   g_strcmp0(sec_type, "sae") != 0) {
+                       DBG("Unsupported security");
+                       return __connman_error_invalid_arguments(msg);
+               }
+
+               mesh_ifname = connman_mesh_get_interface_name();
+
+               if (!connman_mesh_is_interface_created()) {
+                       DBG("Mesh interface doesn't exists");
+                       return __connman_error_invalid_command(msg);
+               }
+
+               str = g_string_sized_new((strlen(name) * 2) + 24);
+
+               for (i = 0; name[i]; i++)
+                       g_string_append_printf(str, "%02x", name[i]);
+
+               g_string_append_printf(str, "_mesh");
+
+               if (g_strcmp0(sec_type, "none") == 0)
+                       g_string_append_printf(str, "_none");
+               else if (g_strcmp0(sec_type, "sae") == 0)
+                       g_string_append_printf(str, "_sae");
+
+               group = g_string_free(str, FALSE);
+
+               identifier = connman_inet_ifaddr(mesh_ifname);
+               address = connman_inet_ifname2addr(mesh_ifname);
+
+               connman_mesh = connman_mesh_create(identifier, group);
+               connman_mesh_set_name(connman_mesh, name);
+               connman_mesh_set_address(connman_mesh, address);
+               connman_mesh_set_security(connman_mesh, sec_type);
+               connman_mesh_set_frequency(connman_mesh, freq);
+               connman_mesh_set_index(connman_mesh, connman_inet_ifindex(mesh_ifname));
+               connman_mesh_set_peer_type(connman_mesh,
+                                          CONNMAN_MESH_PEER_TYPE_CREATED);
+               connman_mesh_set_ieee80211w(connman_mesh, ieee80211w);
+
+               connman_mesh_register(connman_mesh);
+               g_free(group);
+               g_free(identifier);
+               g_free(address);
+               DBG("Successfully Created Mesh Network");
+               return  g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+       } else if (g_str_equal(cmd, "AbortScan")) {
+               DBG("Abort Scan method");
+               err = __connman_device_abort_scan(technology->type);
+               if (err != 0) {
+                       DBG("Failed to abort scan");
+                       return __connman_error_failed(msg, -err);
+               }
+
+               DBG("Successfully requested to abort scan");
+               dbus_message_ref(msg);
+               technology->mesh_dbus_msg = msg;
+
+       } else if (g_str_equal(cmd, "MeshSpecificScan")) {
+               const char *name = NULL;
+               unsigned int freq = 0;
+               dbus_message_iter_recurse(&value, &dict);
+               while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+                       DBusMessageIter entry, value2;
+                       const char *key;
+                       int type;
+
+                       dbus_message_iter_recurse(&dict, &entry);
+
+                       if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                               return __connman_error_invalid_arguments(msg);
+
+                       dbus_message_iter_get_basic(&entry, &key);
+                       dbus_message_iter_next(&entry);
+
+                       if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                               return __connman_error_invalid_arguments(msg);
+
+                       dbus_message_iter_recurse(&entry, &value2);
+
+                       type = dbus_message_iter_get_arg_type(&value2);
+
+                       if (g_str_equal(key, "Name")) {
+                               if (type != DBUS_TYPE_STRING)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &name);
+                       } else if (g_str_equal(key, "Frequency")) {
+                               if (type != DBUS_TYPE_UINT16)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &freq);
+                       }
+                       dbus_message_iter_next(&dict);
+               }
+
+               DBG("MeshID %s Frequency %d sender %s", name, freq,
+                                               dbus_message_get_sender(msg));
+
+               dbus_message_ref(msg);
+               technology->scan_pending =
+                       g_slist_prepend(technology->scan_pending, msg);
+
+               err = __connman_device_request_mesh_specific_scan(technology->type,
+                                                                 name, freq);
+               if (err < 0)
+                       reply_scan_pending(technology, err);
+               else
+                       DBG("Successfully requested to scan specific Mesh Network");
+
+       } else if (g_str_equal(cmd, "SetMeshGate")) {
+               unsigned int hwmp_rootmode = 0;
+               bool gate_announce = false;
+               unsigned int stp = 0;
+               int err;
+               dbus_message_iter_recurse(&value, &dict);
+               while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+                       DBusMessageIter entry, value2;
+                       const char *key;
+                       int type;
+
+                       dbus_message_iter_recurse(&dict, &entry);
+
+                       if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                               return __connman_error_invalid_arguments(msg);
+
+                       dbus_message_iter_get_basic(&entry, &key);
+                       dbus_message_iter_next(&entry);
+
+                       if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                               return __connman_error_invalid_arguments(msg);
+
+                       dbus_message_iter_recurse(&entry, &value2);
+
+                       type = dbus_message_iter_get_arg_type(&value2);
+
+                       if (g_str_equal(key, "GateAnnounce")) {
+                               if (type != DBUS_TYPE_BOOLEAN)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &gate_announce);
+                       } else if (g_str_equal(key, "HWMPRootMode")) {
+                               if (type != DBUS_TYPE_UINT16)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &hwmp_rootmode);
+                       } else if (g_str_equal(key, "STP")) {
+                               if (type != DBUS_TYPE_UINT16)
+                                       return __connman_error_invalid_arguments(msg);
+
+                               dbus_message_iter_get_basic(&value2, &stp);
+                       }
+                       dbus_message_iter_next(&dict);
+               }
+
+               DBG("GateAnnounce %d HWMPRootMode %d STP %d sender %s",
+                   gate_announce, hwmp_rootmode, stp, dbus_message_get_sender(msg));
+
+               err = __connman_mesh_set_stp_gate_announce(gate_announce,
+                                                          hwmp_rootmode,
+                                                          stp);
+
+               if (err < 0)
+                       return __connman_error_failed(msg, -err);
+
+               return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+       } else
+               return __connman_error_invalid_command(msg);
+       return NULL;
+}
+#endif
+
 static const GDBusMethodTable technology_methods[] = {
        { GDBUS_DEPRECATED_METHOD("GetProperties",
                        NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
@@ -1447,6 +1924,11 @@ static const GDBusMethodTable technology_methods[] = {
        { GDBUS_METHOD("GetScanState", NULL, GDBUS_ARGS({ "scan_state", "a{sv}" }),
                        get_scan_state) },
 #endif
+#if defined TIZEN_EXT_WIFI_MESH
+       { GDBUS_ASYNC_METHOD("MeshCommands",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, mesh_commands) },
+#endif
        { },
 };
 
@@ -1557,7 +2039,12 @@ static struct connman_technology *technology_get(enum connman_service_type type)
 
        technology = technology_find(type);
        if (technology) {
+#if defined TIZEN_EXT_WIFI_MESH
+               if (type != CONNMAN_SERVICE_TYPE_P2P &&
+                       type != CONNMAN_SERVICE_TYPE_MESH)
+#else
                if (type != CONNMAN_SERVICE_TYPE_P2P)
+#endif
                        __sync_fetch_and_add(&technology->refcount, 1);
                return technology;
        }
@@ -1588,6 +2075,16 @@ static struct connman_technology *technology_get(enum connman_service_type type)
        technology->path = g_strdup_printf("%s/technology/%s",
                                                        CONNMAN_PATH, str);
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (type == CONNMAN_SERVICE_TYPE_MESH) {
+               struct connman_technology *wifi;
+
+               wifi = technology_find(CONNMAN_SERVICE_TYPE_WIFI);
+               if (wifi)
+                       technology->enabled = wifi->enabled;
+       }
+#endif
+
        technology_load(technology);
        technology_list = g_slist_prepend(technology_list, technology);
        technology->driver_list = tech_drivers;
@@ -1666,6 +2163,13 @@ exist:
                        return -ENOMEM;
        }
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (driver->type == CONNMAN_SERVICE_TYPE_MESH) {
+               if (!technology_get(CONNMAN_SERVICE_TYPE_MESH))
+                       return -ENOMEM;
+       }
+#endif
+
        return 0;
 }
 
@@ -1703,6 +2207,13 @@ void connman_technology_driver_unregister(struct connman_technology_driver *driv
                if (technology)
                        technology_put(technology);
        }
+#if defined TIZEN_EXT_WIFI_MESH
+       if (driver->type == CONNMAN_SERVICE_TYPE_MESH) {
+               technology = technology_find(CONNMAN_SERVICE_TYPE_MESH);
+               if (technology)
+                       technology_put(technology);
+       }
+#endif
 }
 
 void __connman_technology_add_interface(enum connman_service_type type,
@@ -1725,6 +2236,9 @@ void __connman_technology_add_interface(enum connman_service_type type,
        case CONNMAN_SERVICE_TYPE_VPN:
        case CONNMAN_SERVICE_TYPE_GADGET:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        }
 
@@ -1776,6 +2290,9 @@ void __connman_technology_remove_interface(enum connman_service_type type,
        case CONNMAN_SERVICE_TYPE_VPN:
        case CONNMAN_SERVICE_TYPE_GADGET:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        }
 
@@ -1971,6 +2488,15 @@ int __connman_technology_set_offlinemode(bool offlinemode)
        return err;
 }
 
+#if defined TIZEN_EXT_WIFI_MESH
+static gboolean __add_ethernet_to_bridge(gpointer data)
+{
+       DBG("");
+       __connman_mesh_add_ethernet_to_bridge();
+       return FALSE;
+}
+#endif
+
 void __connman_technology_set_connected(enum connman_service_type type,
                bool connected)
 {
@@ -1985,6 +2511,11 @@ void __connman_technology_set_connected(enum connman_service_type type,
 
        technology->connected = connected;
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (technology->type == CONNMAN_SERVICE_TYPE_ETHERNET && connected)
+               g_idle_add(__add_ethernet_to_bridge, NULL);
+#endif
+
        val = connected;
        connman_dbus_property_changed_basic(technology->path,
                        CONNMAN_TECHNOLOGY_INTERFACE, "Connected",
old mode 100755 (executable)
new mode 100644 (file)
index adf6230..a2df55a
@@ -849,6 +849,9 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                return -EOPNOTSUPP;
        }