Add function to get index value from DHCP structure
[framework/connectivity/connman.git] / src / udev.c
index 44e09d5..02e6e53 100644 (file)
 #include <config.h>
 #endif
 
+#include <stdio.h>
+#include <stdlib.h>
 #include <sys/types.h>
 
-#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
 #include <libudev.h>
 
 #include <glib.h>
 
 #include "connman.h"
 
-#ifdef NEED_UDEV_ENUMERATE_ADD_MATCH_PROPERTY
-static int udev_enumerate_add_match_property(struct udev_enumerate *enumerate,
-                                       const char *property, const char *value)
-{
-       return -EINVAL;
-}
-#endif
-
-#ifdef NEED_UDEV_DEVICE_GET_PARENT_WITH_SUBSYSTEM_DEVTYPE
-static struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *device,
-                                               const char *subsystem, const char *devtype)
-{
-       return NULL;
-}
-#endif
+static gboolean rfkill_processing = FALSE;
 
 static GSList *device_list = NULL;
 
-static struct connman_device *find_device(const char *interface)
+static struct connman_device *find_device(int index)
 {
        GSList *list;
 
-       if (interface == NULL)
+       if (index < 0)
                return NULL;
 
        for (list = device_list; list; list = list->next) {
                struct connman_device *device = list->data;
-               const char *device_interface;
-
-               device_interface = connman_device_get_interface(device);
-               if (device_interface == NULL)
-                       continue;
 
-               if (g_str_equal(device_interface, interface) == TRUE)
+               if (connman_device_get_index(device) == index)
                        return device;
        }
 
        return NULL;
 }
 
-static void add_device(struct udev_device *udev_device)
+static void add_net_device(struct udev_device *udev_device)
 {
-       enum connman_device_type devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
-       struct connman_device *device;
        struct udev_list_entry *entry;
-       const char *type = NULL, *interface = NULL;
+       struct connman_device *device;
+       enum connman_device_type devtype;
+       const char *value, *systype;
+       int index = -1;
 
        DBG("");
 
+       systype = udev_device_get_sysattr_value(udev_device, "type");
+       if (systype == NULL || atoi(systype) != 1)
+               return;
+
        entry = udev_device_get_properties_list_entry(udev_device);
        while (entry) {
                const char *name = udev_list_entry_get_name(entry);
 
-               if (g_str_has_prefix(name, "CONNMAN_TYPE") == TRUE)
-                       type = udev_list_entry_get_value(entry);
-               else if (g_str_has_prefix(name, "CONNMAN_INTERFACE") == TRUE)
-                       interface = udev_list_entry_get_value(entry);
+               if (g_str_has_prefix(name, "IFINDEX") == TRUE) {
+                       const char *value = udev_list_entry_get_value(entry);
+                       if (value != NULL)
+                               index = atoi(value);
+               }
 
                entry = udev_list_entry_get_next(entry);
        }
 
-       device = find_device(interface);
-       if (device != NULL)
+       if (index < 0)
                return;
 
-       if (type == NULL || interface == NULL)
-               return;
+       devtype = __connman_inet_get_device_type(index);
 
-       if (g_str_equal(interface, "ttyUSB0") == FALSE &&
-                               g_str_equal(interface, "noz0") == FALSE)
+       switch (devtype) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+       case CONNMAN_DEVICE_TYPE_CELLULAR:
+       case CONNMAN_DEVICE_TYPE_GPS:
                return;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_MBM:
+       case CONNMAN_DEVICE_TYPE_HSO:
+               break;
+       }
 
-       if (g_str_equal(type, "nozomi") == TRUE)
-               devtype = CONNMAN_DEVICE_TYPE_NOZOMI;
-       else if (g_str_equal(type, "huawei") == TRUE)
-               devtype = CONNMAN_DEVICE_TYPE_HUAWEI;
-       else if (g_str_equal(type, "novatel") == TRUE)
-               devtype = CONNMAN_DEVICE_TYPE_NOVATEL;
-       else
+       device = find_device(index);
+       if (device != NULL)
                return;
 
-       device = connman_device_create(interface, devtype);
+       device = connman_inet_create_device(index);
        if (device == NULL)
                return;
 
-       connman_device_set_mode(device, CONNMAN_DEVICE_MODE_NETWORK_SINGLE);
-       connman_device_set_policy(device, CONNMAN_DEVICE_POLICY_MANUAL);
-
-       connman_device_set_interface(device, interface);
+       value = udev_device_get_sysattr_value(udev_device, "phy80211/index");
+       if (value != NULL)
+               __connman_device_set_phyindex(device, atoi(value));
 
        if (connman_device_register(device) < 0) {
                connman_device_unref(device);
@@ -130,11 +121,11 @@ static void add_device(struct udev_device *udev_device)
        device_list = g_slist_append(device_list, device);
 }
 
-static void remove_device(struct udev_device *udev_device)
+static void remove_net_device(struct udev_device *udev_device)
 {
-       struct connman_device *device;
        struct udev_list_entry *entry;
-       const char *interface = NULL;
+       struct connman_device *device;
+       int index = -1;
 
        DBG("");
 
@@ -142,13 +133,19 @@ static void remove_device(struct udev_device *udev_device)
        while (entry) {
                const char *name = udev_list_entry_get_name(entry);
 
-               if (g_str_has_prefix(name, "CONNMAN_INTERFACE") == TRUE)
-                       interface = udev_list_entry_get_value(entry);
+               if (g_str_has_prefix(name, "IFINDEX") == TRUE) {
+                       const char *value = udev_list_entry_get_value(entry);
+                       if (value != NULL)
+                               index = atoi(value);
+               }
 
                entry = udev_list_entry_get_next(entry);
        }
 
-       device = find_device(interface);
+       if (index < 0)
+               return;
+
+       device = find_device(index);
        if (device == NULL)
                return;
 
@@ -158,6 +155,70 @@ static void remove_device(struct udev_device *udev_device)
        connman_device_unref(device);
 }
 
+static void phyindex_rfkill(int phyindex, connman_bool_t blocked)
+{
+       GSList *list;
+
+       if (phyindex < 0)
+               return;
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+
+               if (__connman_device_get_phyindex(device) == phyindex)
+                       __connman_device_set_blocked(device, blocked);
+       }
+}
+
+static void change_rfkill_device(struct udev_device *device)
+{
+       struct udev_device *parent;
+       struct udev_list_entry *entry;
+       connman_bool_t blocked;
+       const char *value, *type = NULL;
+       int state = -1;
+
+       if (rfkill_processing == FALSE)
+               return;
+
+       entry = udev_device_get_properties_list_entry(device);
+       while (entry) {
+               const char *name = udev_list_entry_get_name(entry);
+
+               if (g_str_has_prefix(name, "RFKILL_STATE") == TRUE) {
+                       value = udev_list_entry_get_value(entry);
+                       if (value != NULL)
+                               state = atoi(value);
+               } else if (g_str_has_prefix(name, "RFKILL_TYPE") == TRUE)
+                       type = udev_list_entry_get_value(entry);
+
+               entry = udev_list_entry_get_next(entry);
+       }
+
+       if (type == NULL || state < 0)
+               return;
+
+       if (g_str_equal(type, "wlan") == FALSE)
+               return;
+
+       parent = udev_device_get_parent(device);
+       if (parent == NULL)
+               return;
+
+       value = udev_device_get_sysattr_value(parent, "index");
+       if (value == NULL)
+               return;
+
+       blocked = (state != 1) ? TRUE : FALSE;
+
+       phyindex_rfkill(atoi(value), blocked);
+}
+
+static void add_rfkill_device(struct udev_device *device)
+{
+       change_rfkill_device(device);
+}
+
 static void print_properties(struct udev_device *device, const char *prefix)
 {
        struct udev_list_entry *entry;
@@ -172,9 +233,11 @@ static void print_properties(struct udev_device *device, const char *prefix)
                                g_str_has_prefix(name, "ID_MODEM") == TRUE ||
                                g_str_equal(name, "ID_VENDOR") == TRUE ||
                                g_str_equal(name, "ID_MODEL") == TRUE ||
+                               g_str_equal(name, "INTERFACE") == TRUE ||
+                               g_str_equal(name, "IFINDEX") == TRUE ||
                                g_str_equal(name, "DEVNAME") == TRUE ||
                                g_str_equal(name, "DEVPATH") == TRUE)
-                       connman_debug("%s%s = %s", prefix, name, value);
+                       DBG("%s%s = %s", prefix, name, value);
 
                entry = udev_list_entry_get_next(entry);
        }
@@ -182,10 +245,10 @@ static void print_properties(struct udev_device *device, const char *prefix)
 
 static void print_device(struct udev_device *device, const char *action)
 {
-       const char *subsystem, *devtype = NULL;
+       const char *subsystem, *sysname, *driver, *devtype = NULL;
        struct udev_device *parent;
 
-       connman_debug("=== %s ===", action);
+       DBG("=== %s ===", action);
        print_properties(device, "");
 
        parent = udev_device_get_parent(device);
@@ -203,6 +266,18 @@ static void print_device(struct udev_device *device, const char *action)
        parent = udev_device_get_parent_with_subsystem_devtype(device,
                                                        subsystem, devtype);
        print_properties(parent, "    ");
+
+       driver = udev_device_get_driver(device);
+       if (driver == NULL) {
+               driver = udev_device_get_driver(parent);
+               if (driver == NULL)
+                       return;
+       }
+
+       devtype = udev_device_get_devtype(device);
+       sysname = udev_device_get_sysname(device);
+
+       driver = udev_device_get_driver(parent);
 }
 
 static void enumerate_devices(struct udev *context)
@@ -214,7 +289,8 @@ static void enumerate_devices(struct udev *context)
        if (enumerate == NULL)
                return;
 
-       udev_enumerate_add_match_property(enumerate, "CONNMAN_TYPE", "?*");
+       udev_enumerate_add_match_subsystem(enumerate, "net");
+       udev_enumerate_add_match_subsystem(enumerate, "rfkill");
 
        udev_enumerate_scan_devices(enumerate);
 
@@ -224,12 +300,20 @@ static void enumerate_devices(struct udev *context)
                struct udev_device *device;
 
                device = udev_device_new_from_syspath(context, syspath);
+               if (device != NULL) {
+                       const char *subsystem;
 
-               print_device(device, "coldplug");
+                       print_device(device, "coldplug");
 
-               add_device(device);
+                       subsystem = udev_device_get_subsystem(device);
 
-               udev_device_unref(device);
+                       if (g_strcmp0(subsystem, "net") == 0)
+                               add_net_device(device);
+                       else if (g_strcmp0(subsystem, "rfkill") == 0)
+                               add_rfkill_device(device);
+
+                       udev_device_unref(device);
+               }
 
                entry = udev_list_entry_get_next(entry);
        }
@@ -242,22 +326,34 @@ static gboolean udev_event(GIOChannel *channel,
 {
        struct udev_monitor *monitor = user_data;
        struct udev_device *device;
-       const char *action;
+       const char *subsystem, *action;
 
        device = udev_monitor_receive_device(monitor);
        if (device == NULL)
                return TRUE;
 
+       subsystem = udev_device_get_subsystem(device);
+       if (subsystem == NULL)
+               goto done;
+
        action = udev_device_get_action(device);
        if (action == NULL)
                goto done;
 
        print_device(device, action);
 
-       if (g_str_equal(action, "add") == TRUE)
-               add_device(device);
-       else if (g_str_equal(action, "remove") == TRUE)
-               remove_device(device);
+       if (g_str_equal(action, "add") == TRUE) {
+               if (g_str_equal(subsystem, "net") == TRUE)
+                       add_net_device(device);
+               else if (g_str_equal(subsystem, "rfkill") == TRUE)
+                       add_rfkill_device(device);
+       } else if (g_str_equal(action, "remove") == TRUE) {
+               if (g_str_equal(subsystem, "net") == TRUE)
+                       remove_net_device(device);
+       } else if (g_str_equal(action, "change") == TRUE) {
+               if (g_str_equal(subsystem, "rfkill") == TRUE)
+                       change_rfkill_device(device);
+       }
 
 done:
        udev_device_unref(device);
@@ -269,11 +365,114 @@ static struct udev *udev_ctx;
 static struct udev_monitor *udev_mon;
 static guint udev_watch = 0;
 
-int __connman_udev_init(void)
+void __connman_udev_enable_rfkill_processing(void)
 {
-       GIOChannel *channel;
-       int fd;
+       rfkill_processing = TRUE;
+
+       connman_warn("Enabling udev based RFKILL processing");
 
+       enumerate_devices(udev_ctx);
+}
+
+char *__connman_udev_get_devtype(const char *ifname)
+{
+       struct udev_device *device;
+       const char *devtype;
+
+       device = udev_device_new_from_subsystem_sysname(udev_ctx,
+                                                       "net", ifname);
+       if (device == NULL)
+               return NULL;
+
+       devtype = udev_device_get_devtype(device);
+       if (devtype == NULL)
+               goto done;
+
+done:
+       udev_device_unref(device);
+
+       return NULL;
+}
+
+char *__connman_udev_get_mbm_devnode(const char *ifname)
+{
+       struct udev_device *device, *parent, *control;
+       const char *driver, *devpath1, *devpath2;
+       char *devnode = NULL;
+       unsigned int i;
+
+       device = udev_device_new_from_subsystem_sysname(udev_ctx,
+                                                       "net", ifname);
+       if (device == NULL)
+               return NULL;
+
+       parent = udev_device_get_parent(device);
+       if (parent == NULL)
+               goto done;
+
+       driver = udev_device_get_driver(parent);
+       if (g_strcmp0(driver, "cdc_ether") != 0)
+               goto done;
+
+       parent = udev_device_get_parent_with_subsystem_devtype(device,
+                                                       "usb", "usb_device");
+       if (parent == NULL)
+               goto done;
+
+       devpath1 = udev_device_get_devpath(parent);
+
+       for (i = 0; i < 64; i++) {
+               char sysname[16];
+
+               snprintf(sysname, sizeof(sysname) - 1, "ttyACM%d", i);
+
+               control = udev_device_new_from_subsystem_sysname(udev_ctx,
+                                                       "tty", sysname);
+               if (control == NULL)
+                       continue;
+
+               parent = udev_device_get_parent_with_subsystem_devtype(control,
+                                                       "usb", "usb_device");
+               if (parent == NULL)
+                       continue;
+
+               devpath2 = udev_device_get_devpath(parent);
+
+               if (g_strcmp0(devpath1, devpath2) == 0) {
+                       devnode = g_strdup(udev_device_get_devnode(control));
+                       break;
+               }
+       }
+
+done:
+       udev_device_unref(device);
+
+       return devnode;
+}
+
+void __connman_udev_rfkill(const char *sysname, connman_bool_t blocked)
+{
+       struct udev_device *device, *parent;
+       const char *value;
+
+       device = udev_device_new_from_subsystem_sysname(udev_ctx,
+                                                       "rfkill", sysname);
+       if (device == NULL)
+               return;
+
+       parent = udev_device_get_parent(device);
+       if (parent == NULL)
+               return;
+
+       value = udev_device_get_sysattr_value(parent, "index");
+       if (value == NULL)
+               return;
+
+       phyindex_rfkill(atoi(value), blocked);
+}
+
+int __connman_udev_init(void)
+{
        DBG("");
 
        udev_ctx = udev_new();
@@ -282,8 +481,7 @@ int __connman_udev_init(void)
                return -1;
        }
 
-       udev_mon = udev_monitor_new_from_socket(udev_ctx,
-                                               "@/org/moblin/connman/udev");
+       udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev");
        if (udev_mon == NULL) {
                connman_error("Failed to create udev monitor");
                udev_unref(udev_ctx);
@@ -291,12 +489,26 @@ int __connman_udev_init(void)
                return -1;
        }
 
+       udev_monitor_filter_add_match_subsystem_devtype(udev_mon,
+                                                       "net", NULL);
+       udev_monitor_filter_add_match_subsystem_devtype(udev_mon,
+                                                       "rfkill", NULL);
+
+       udev_monitor_filter_update(udev_mon);
+
+       return 0;
+}
+
+void __connman_udev_start(void)
+{
+       GIOChannel *channel;
+       int fd;
+
+       DBG("");
+
        if (udev_monitor_enable_receiving(udev_mon) < 0) {
                connman_error("Failed to enable udev monitor");
-               udev_unref(udev_ctx);
-               udev_ctx = NULL;
-               udev_monitor_unref(udev_mon);
-               return -1;
+               return;
        }
 
        enumerate_devices(udev_ctx);
@@ -305,13 +517,11 @@ int __connman_udev_init(void)
 
        channel = g_io_channel_unix_new(fd);
        if (channel == NULL)
-               return 0;
+               return;
 
        udev_watch = g_io_add_watch(channel, G_IO_IN, udev_event, udev_mon);
 
        g_io_channel_unref(channel);
-
-       return 0;
 }
 
 void __connman_udev_cleanup(void)
@@ -336,6 +546,8 @@ void __connman_udev_cleanup(void)
        if (udev_ctx == NULL)
                return;
 
+       udev_monitor_filter_remove(udev_mon);
+
        udev_monitor_unref(udev_mon);
        udev_unref(udev_ctx);
 }