[PATCH 02/10] kernel-device: new generic kernel device object, with a udev backend

Aleksander Morgado aleksander at aleksander.es
Sat Aug 6 13:03:30 UTC 2016


Instead of relying constantly on GUdevDevice objects reported by GUdev, we now
use a new generic object (MMKernelDevice) for which we provide an initial GUdev
based backend.

This will help make udev monitoring support optional during compilation.
---
 plugins/Makefile.am                      |   1 +
 plugins/cinterion/mm-plugin-cinterion.c  |   4 +-
 plugins/dell/mm-plugin-dell.c            |   8 +-
 plugins/haier/mm-plugin-haier.c          |   4 +-
 plugins/huawei/mm-plugin-huawei.c        |  20 +-
 plugins/longcheer/mm-plugin-longcheer.c  |   6 +-
 plugins/mbm/mm-plugin-mbm.c              |   4 +-
 plugins/mtk/mm-plugin-mtk.c              |   6 +-
 plugins/nokia/mm-plugin-nokia-icera.c    |   6 +-
 plugins/option/mm-plugin-hso.c           |   6 +-
 plugins/option/mm-plugin-option.c        |   4 +-
 plugins/simtech/mm-plugin-simtech.c      |   8 +-
 plugins/telit/mm-common-telit.c          |  24 +-
 plugins/x22x/mm-plugin-x22x.c            |   6 +-
 plugins/zte/mm-plugin-zte.c              |   8 +-
 src/Makefile.am                          |  15 +
 src/kerneldevice/mm-kernel-device-udev.c | 614 +++++++++++++++++++++++++++++++
 src/kerneldevice/mm-kernel-device-udev.h |  48 +++
 src/kerneldevice/mm-kernel-device.c      | 179 +++++++++
 src/kerneldevice/mm-kernel-device.h      |  89 +++++
 src/mm-base-manager.c                    | 231 +++---------
 src/mm-device.c                          | 341 ++++-------------
 src/mm-device.h                          |  96 +++--
 src/mm-plugin-manager.c                  |  38 +-
 src/mm-plugin.c                          |  78 ++--
 src/mm-plugin.h                          |  10 +-
 src/mm-port-probe.c                      | 205 +++++------
 src/mm-port-probe.h                      |  20 +-
 28 files changed, 1336 insertions(+), 743 deletions(-)
 create mode 100644 src/kerneldevice/mm-kernel-device-udev.c
 create mode 100644 src/kerneldevice/mm-kernel-device-udev.h
 create mode 100644 src/kerneldevice/mm-kernel-device.c
 create mode 100644 src/kerneldevice/mm-kernel-device.h

diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 31d222a..435d0ed 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -12,6 +12,7 @@ AM_CFLAGS = \
 	-I$(top_srcdir) \
 	-I$(top_srcdir)/src \
 	-I$(top_builddir)/src \
+	-I$(top_srcdir)/src/kerneldevice \
 	-I$(top_srcdir)/include \
 	-I$(top_builddir)/include \
 	-I$(top_srcdir)/libmm-glib \
diff --git a/plugins/cinterion/mm-plugin-cinterion.c b/plugins/cinterion/mm-plugin-cinterion.c
index d65ad53..4492cda 100644
--- a/plugins/cinterion/mm-plugin-cinterion.c
+++ b/plugins/cinterion/mm-plugin-cinterion.c
@@ -176,8 +176,8 @@ grab_port (MMPlugin *self,
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
         pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
-    } else if (g_udev_device_get_property_as_boolean (mm_port_probe_peek_port (probe),
-                                                      "ID_MM_CINTERION_PORT_TYPE_GPS")) {
+    } else if (mm_kernel_device_get_property_as_boolean (mm_port_probe_peek_port (probe),
+                                                         "ID_MM_CINTERION_PORT_TYPE_GPS")) {
         mm_dbg ("(%s/%s)' Port flagged as GPS",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
diff --git a/plugins/dell/mm-plugin-dell.c b/plugins/dell/mm-plugin-dell.c
index c40f40d..290faa8 100644
--- a/plugins/dell/mm-plugin-dell.c
+++ b/plugins/dell/mm-plugin-dell.c
@@ -324,9 +324,9 @@ dell_custom_init (MMPortProbe *probe,
                   gpointer user_data)
 {
     CustomInitContext *ctx;
-    GUdevDevice *udevDevice;
+    MMKernelDevice *port_device;
 
-    udevDevice = mm_port_probe_peek_port (probe);
+    port_device = mm_port_probe_peek_port (probe);
 
     ctx = g_slice_new0 (CustomInitContext);
     ctx->result = g_simple_async_result_new (G_OBJECT (probe),
@@ -342,7 +342,7 @@ dell_custom_init (MMPortProbe *probe,
 
     /* Dell-branded Telit modems always answer to +GMI
      * Avoid +CGMI and ATI sending for minimizing port probing time */
-    if (g_udev_device_get_property_as_boolean (udevDevice, "ID_MM_TELIT_PORTS_TAGGED")) {
+    if (mm_kernel_device_get_property_as_boolean (port_device, "ID_MM_TELIT_PORTS_TAGGED")) {
         ctx->cgmi_retries = 0;
         ctx->ati_retries = 0;
     }
@@ -420,7 +420,7 @@ create_modem (MMPlugin *self,
 
     if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_TELIT)) {
         mm_dbg ("Telit-powered Dell-branded modem found...");
-        return MM_BASE_MODEM (mm_broadband_modem_telit_new (sysfs_path,
+        return MM_BASE_MODEM (mm_broadband_modem_telit_new (uid,
                                                             drivers,
                                                             mm_plugin_get_name (self),
                                                             vendor,
diff --git a/plugins/haier/mm-plugin-haier.c b/plugins/haier/mm-plugin-haier.c
index c56a148..eb0d284 100644
--- a/plugins/haier/mm-plugin-haier.c
+++ b/plugins/haier/mm-plugin-haier.c
@@ -52,13 +52,13 @@ grab_port (MMPlugin *self,
            MMPortProbe *probe,
            GError **error)
 {
-    GUdevDevice *port;
+    MMKernelDevice *port;
     MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
 
     port = mm_port_probe_peek_port (probe);
 
     /* Look for port type hints */
-    if (mm_port_probe_is_at (probe) && g_udev_device_get_property_as_boolean (port, "ID_MM_HAIER_PORT_TYPE_MODEM")) {
+    if (mm_port_probe_is_at (probe) && mm_kernel_device_get_property_as_boolean (port, "ID_MM_HAIER_PORT_TYPE_MODEM")) {
         mm_dbg ("HAIER: AT port '%s/%s' flagged as primary",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
diff --git a/plugins/huawei/mm-plugin-huawei.c b/plugins/huawei/mm-plugin-huawei.c
index 3bb303a..2ec2ca6 100644
--- a/plugins/huawei/mm-plugin-huawei.c
+++ b/plugins/huawei/mm-plugin-huawei.c
@@ -229,7 +229,7 @@ try_next_usbif (MMDevice *device)
         if (g_str_equal (mm_port_probe_get_port_subsys (probe), "tty")) {
             gint usbif;
 
-            usbif = g_udev_device_get_property_as_int (mm_port_probe_peek_port (probe), "ID_USB_INTERFACE_NUM");
+            usbif = mm_kernel_device_get_property_as_int (mm_port_probe_peek_port (probe), "ID_USB_INTERFACE_NUM");
             if (usbif == fi_ctx->first_usbif) {
                 /* This is the one we just probed, which wasn't yet removed, so just skip it */
             } else if (usbif > fi_ctx->first_usbif &&
@@ -254,7 +254,7 @@ static void
 huawei_custom_init_step (HuaweiCustomInitContext *ctx)
 {
     FirstInterfaceContext *fi_ctx;
-    GUdevDevice *port;
+    MMKernelDevice *port;
 
     /* If cancelled, end */
     if (g_cancellable_is_cancelled (ctx->cancellable)) {
@@ -292,7 +292,7 @@ huawei_custom_init_step (HuaweiCustomInitContext *ctx)
 
     /* Try to get a port map from the modem */
     port = mm_port_probe_peek_port (ctx->probe);
-    if (!ctx->getportmode_done && !g_udev_device_get_property_as_boolean (port, "ID_MM_HUAWEI_DISABLE_GETPORTMODE")) {
+    if (!ctx->getportmode_done && !mm_kernel_device_get_property_as_boolean (port, "ID_MM_HUAWEI_DISABLE_GETPORTMODE")) {
         if (ctx->getportmode_retries == 0) {
             g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
             huawei_custom_init_context_complete_and_free (ctx);
@@ -390,8 +390,8 @@ huawei_custom_init (MMPortProbe *probe,
     ctx->getportmode_retries = 3;
 
     /* Custom init only to be run in the first interface */
-    if (g_udev_device_get_property_as_int (mm_port_probe_peek_port (probe),
-                                           "ID_USB_INTERFACE_NUM") != fi_ctx->first_usbif) {
+    if (mm_kernel_device_get_property_as_int (mm_port_probe_peek_port (probe),
+                                              "ID_USB_INTERFACE_NUM") != fi_ctx->first_usbif) {
 
         if (fi_ctx->custom_init_run)
             /* If custom init was run already, we can consider this as successfully run */
@@ -433,7 +433,7 @@ propagate_port_mode_results (GList *probes)
         MMPortSerialAtFlag at_port_flags = MM_PORT_SERIAL_AT_FLAG_NONE;
         gint usbif;
 
-        usbif = g_udev_device_get_property_as_int (mm_port_probe_peek_port (MM_PORT_PROBE (l->data)), "ID_USB_INTERFACE_NUM");
+        usbif = mm_kernel_device_get_property_as_int (mm_port_probe_peek_port (MM_PORT_PROBE (l->data)), "ID_USB_INTERFACE_NUM");
 
         if (GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_GETPORTMODE_SUPPORTED))) {
             if (usbif + 1 == GPOINTER_TO_INT (g_object_get_data (G_OBJECT (device), TAG_HUAWEI_PCUI_PORT))) {
@@ -526,23 +526,23 @@ grab_port (MMPlugin *self,
            GError **error)
 {
     MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
-    GUdevDevice *port;
+    MMKernelDevice *port;
     MMPortType port_type;
 
     port_type = mm_port_probe_get_port_type (probe);
     port = mm_port_probe_peek_port (probe);
 
-    if (g_udev_device_get_property_as_boolean (port, "ID_MM_HUAWEI_AT_PORT")) {
+    if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_HUAWEI_AT_PORT")) {
         mm_dbg ("(%s/%s)' Port flagged as primary",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
         pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
-    } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_HUAWEI_MODEM_PORT")) {
+    } else if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_HUAWEI_MODEM_PORT")) {
         mm_dbg ("(%s/%s) Port flagged as PPP",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
         pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
-    } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_HUAWEI_GPS_PORT")) {
+    } else if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_HUAWEI_GPS_PORT")) {
         mm_dbg ("(%s/%s) Port flagged as GPS",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
diff --git a/plugins/longcheer/mm-plugin-longcheer.c b/plugins/longcheer/mm-plugin-longcheer.c
index ea11070..1b692c1 100644
--- a/plugins/longcheer/mm-plugin-longcheer.c
+++ b/plugins/longcheer/mm-plugin-longcheer.c
@@ -206,7 +206,7 @@ grab_port (MMPlugin *self,
            MMPortProbe *probe,
            GError **error)
 {
-    GUdevDevice *port;
+    MMKernelDevice *port;
     MMPortType ptype;
     MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
 
@@ -217,12 +217,12 @@ grab_port (MMPlugin *self,
      * be the data/primary port on these devices.  We have to tag them based on
      * what the Windows .INF files say the port layout should be.
      */
-    if (g_udev_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_PORT_TYPE_MODEM")) {
+    if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_PORT_TYPE_MODEM")) {
         mm_dbg ("longcheer: AT port '%s/%s' flagged as primary",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
         pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
-    } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_PORT_TYPE_AUX")) {
+    } else if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_PORT_TYPE_AUX")) {
         mm_dbg ("longcheer: AT port '%s/%s' flagged as secondary",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
diff --git a/plugins/mbm/mm-plugin-mbm.c b/plugins/mbm/mm-plugin-mbm.c
index b4b2752..af9b163 100644
--- a/plugins/mbm/mm-plugin-mbm.c
+++ b/plugins/mbm/mm-plugin-mbm.c
@@ -72,13 +72,13 @@ grab_port (MMPlugin *self,
            GError **error)
 {
     MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
-    GUdevDevice *port;
+    MMKernelDevice *port;
     MMPortType port_type;
 
     port_type = mm_port_probe_get_port_type (probe);
     port = mm_port_probe_peek_port (probe);
 
-    if (g_udev_device_get_property_as_boolean (port, "ID_MM_ERICSSON_MBM_GPS_PORT")) {
+    if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_ERICSSON_MBM_GPS_PORT")) {
         mm_dbg ("(%s/%s) Port flagged as GPS",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
diff --git a/plugins/mtk/mm-plugin-mtk.c b/plugins/mtk/mm-plugin-mtk.c
index 080aef2..93b4c0c 100644
--- a/plugins/mtk/mm-plugin-mtk.c
+++ b/plugins/mtk/mm-plugin-mtk.c
@@ -56,19 +56,19 @@ grab_port (MMPlugin *self,
            MMPortProbe *probe,
            GError **error)
 {
-    GUdevDevice *port;
+    MMKernelDevice *port;
     MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
 
     port = mm_port_probe_peek_port (probe);
 
     if (mm_port_probe_is_at (probe)) {
         /* Get port type from udev */
-        if (g_udev_device_get_property_as_boolean (port, "ID_MM_MTK_AT_PORT")) {
+        if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_MTK_AT_PORT")) {
             mm_dbg ("MTK: AT port '%s/%s' flagged as secondary",
                     mm_port_probe_get_port_subsys (probe),
                     mm_port_probe_get_port_name (probe));
             pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
-        } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_MTK_MODEM_PORT")) {
+        } else if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_MTK_MODEM_PORT")) {
             mm_dbg ("MTK: Modem port '%s/%s' flagged as primary",
                     mm_port_probe_get_port_subsys (probe),
                     mm_port_probe_get_port_name (probe));
diff --git a/plugins/nokia/mm-plugin-nokia-icera.c b/plugins/nokia/mm-plugin-nokia-icera.c
index 151a4ec..e875cf4 100644
--- a/plugins/nokia/mm-plugin-nokia-icera.c
+++ b/plugins/nokia/mm-plugin-nokia-icera.c
@@ -63,19 +63,19 @@ grab_port (MMPlugin *self,
            MMPortProbe *probe,
            GError **error)
 {
-    GUdevDevice *port;
+    MMKernelDevice *port;
     MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
 
     port = mm_port_probe_peek_port (probe);
 
     /* Look for port type hints */
     if (mm_port_probe_is_at (probe)) {
-        if (g_udev_device_get_property_as_boolean (port, "ID_MM_NOKIA_PORT_TYPE_MODEM")) {
+        if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_NOKIA_PORT_TYPE_MODEM")) {
             mm_dbg ("Nokia: AT port '%s/%s' flagged as primary",
                     mm_port_probe_get_port_subsys (probe),
                     mm_port_probe_get_port_name (probe));
             pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
-        } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_NOKIA_PORT_TYPE_AUX")) {
+        } else if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_NOKIA_PORT_TYPE_AUX")) {
             mm_dbg ("Nokia: AT port '%s/%s' flagged as secondary",
                     mm_port_probe_get_port_subsys (probe),
                     mm_port_probe_get_port_name (probe));
diff --git a/plugins/option/mm-plugin-hso.c b/plugins/option/mm-plugin-hso.c
index 5b9bcbd..c606cc9 100644
--- a/plugins/option/mm-plugin-hso.c
+++ b/plugins/option/mm-plugin-hso.c
@@ -56,13 +56,13 @@ hso_custom_init (MMPortProbe *probe,
                  GAsyncReadyCallback callback,
                  gpointer user_data)
 {
-    GUdevDevice *udev_port;
+    MMKernelDevice *kernel_port;
     GSimpleAsyncResult *result;
     const gchar *subsys, *sysfs_path;
 
     subsys = mm_port_probe_get_port_subsys (probe);
-    udev_port = mm_port_probe_peek_port (probe);
-    sysfs_path = g_udev_device_get_sysfs_path (udev_port);
+    kernel_port = mm_port_probe_peek_port (probe);
+    sysfs_path = mm_kernel_device_get_sysfs_path (kernel_port);
 
     if (g_str_equal (subsys, "tty")) {
         gchar *hsotype_path;
diff --git a/plugins/option/mm-plugin-option.c b/plugins/option/mm-plugin-option.c
index 90184cc..6040e3d 100644
--- a/plugins/option/mm-plugin-option.c
+++ b/plugins/option/mm-plugin-option.c
@@ -55,7 +55,7 @@ grab_port (MMPlugin *self,
            GError **error)
 {
     MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
-    GUdevDevice *port;
+    MMKernelDevice *port;
     gint usbif;
 
     /* The Option plugin cannot do anything with non-AT ports */
@@ -73,7 +73,7 @@ grab_port (MMPlugin *self,
      * the modem/data port, per mail with Option engineers.  Only this port
      * will emit responses to dialing commands.
      */
-    usbif = g_udev_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM");
+    usbif = mm_kernel_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM");
     if (usbif == 0)
         pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY | MM_PORT_SERIAL_AT_FLAG_PPP;
 
diff --git a/plugins/simtech/mm-plugin-simtech.c b/plugins/simtech/mm-plugin-simtech.c
index aa22414..b573d6e 100644
--- a/plugins/simtech/mm-plugin-simtech.c
+++ b/plugins/simtech/mm-plugin-simtech.c
@@ -69,7 +69,7 @@ grab_port (MMPlugin *self,
            MMPortProbe *probe,
            GError **error)
 {
-    GUdevDevice *port;
+    MMKernelDevice *port;
     MMPortType ptype;
     MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
 
@@ -81,12 +81,12 @@ grab_port (MMPlugin *self,
          * be the data/primary port on these devices.  We have to tag them based on
          * what the Windows .INF files say the port layout should be.
          */
-        if (g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_PORT_TYPE_MODEM")) {
+        if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_SIMTECH_PORT_TYPE_MODEM")) {
             mm_dbg ("Simtech: AT port '%s/%s' flagged as primary",
                     mm_port_probe_get_port_subsys (probe),
                     mm_port_probe_get_port_name (probe));
             pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
-        } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_PORT_TYPE_AUX")) {
+        } else if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_SIMTECH_PORT_TYPE_AUX")) {
             mm_dbg ("Simtech: AT port '%s/%s' flagged as secondary",
                     mm_port_probe_get_port_subsys (probe),
                     mm_port_probe_get_port_name (probe));
@@ -98,7 +98,7 @@ grab_port (MMPlugin *self,
          * to show up with more than two AT-capable ports.
          */
         if (pflags == MM_PORT_SERIAL_AT_FLAG_NONE &&
-            g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_TAGGED"))
+            mm_kernel_device_get_property_as_boolean (port, "ID_MM_SIMTECH_TAGGED"))
             ptype = MM_PORT_TYPE_IGNORED;
     }
 
diff --git a/plugins/telit/mm-common-telit.c b/plugins/telit/mm-common-telit.c
index e607f2e..eaa06b0 100644
--- a/plugins/telit/mm-common-telit.c
+++ b/plugins/telit/mm-common-telit.c
@@ -32,7 +32,7 @@ telit_grab_port (MMPlugin *self,
                  MMPortProbe *probe,
                  GError **error)
 {
-    GUdevDevice *port;
+    MMKernelDevice *port;
     MMDevice *device;
     MMPortType ptype;
     MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
@@ -48,33 +48,33 @@ telit_grab_port (MMPlugin *self,
      * If no udev rules are found, AT#PORTCFG (if supported) can be used for
      * identifying the port layout
      */
-    if (g_udev_device_get_property_as_boolean (port, TAG_TELIT_MODEM_PORT)) {
+    if (mm_kernel_device_get_property_as_boolean (port, TAG_TELIT_MODEM_PORT)) {
         mm_dbg ("telit: AT port '%s/%s' flagged as primary",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
         pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
-    } else if (g_udev_device_get_property_as_boolean (port, TAG_TELIT_AUX_PORT)) {
+    } else if (mm_kernel_device_get_property_as_boolean (port, TAG_TELIT_AUX_PORT)) {
         mm_dbg ("telit: AT port '%s/%s' flagged as secondary",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
         pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
-    } else if (g_udev_device_get_property_as_boolean (port, TAG_TELIT_NMEA_PORT)) {
+    } else if (mm_kernel_device_get_property_as_boolean (port, TAG_TELIT_NMEA_PORT)) {
         mm_dbg ("telit: port '%s/%s' flagged as NMEA",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
         ptype = MM_PORT_TYPE_GPS;
     } else if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) != NULL) {
-        if (g_strcmp0 (g_udev_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT)) == 0) {
+        if (g_strcmp0 (mm_kernel_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT)) == 0) {
             mm_dbg ("telit: AT port '%s/%s' flagged as primary",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
             pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
-        } else if (g_strcmp0 (g_udev_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_AUX_PORT)) == 0) {
+        } else if (g_strcmp0 (mm_kernel_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_AUX_PORT)) == 0) {
             mm_dbg ("telit: AT port '%s/%s' flagged as secondary",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
             pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
-        } else if (g_strcmp0 (g_udev_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT)) == 0) {
+        } else if (g_strcmp0 (mm_kernel_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT)) == 0) {
             mm_dbg ("telit: port '%s/%s' flagged as NMEA",
                 mm_port_probe_get_port_subsys (probe),
                 mm_port_probe_get_port_name (probe));
@@ -245,7 +245,7 @@ telit_custom_init_context_complete_and_free (TelitCustomInitContext *ctx)
 static void
 telit_custom_init_step (TelitCustomInitContext *ctx)
 {
-    GUdevDevice *port;
+    MMKernelDevice *port;
 
     /* If cancelled, end */
     if (g_cancellable_is_cancelled (ctx->cancellable)) {
@@ -259,7 +259,7 @@ telit_custom_init_step (TelitCustomInitContext *ctx)
      */
     port = mm_port_probe_peek_port (ctx->probe);
     if (!ctx->getportcfg_done &&
-        g_strcmp0 (g_udev_device_get_property (port, "ID_USB_INTERFACE_NUM"), "00") == 0) {
+        g_strcmp0 (mm_kernel_device_get_property (port, "ID_USB_INTERFACE_NUM"), "00") == 0) {
 
         if (ctx->getportcfg_retries == 0)
             goto out;
@@ -290,11 +290,11 @@ telit_custom_init (MMPortProbe *probe,
                    gpointer user_data)
 {
     MMDevice *device;
-    GUdevDevice *udevDevice;
+    MMKernelDevice *port_device;
     TelitCustomInitContext *ctx;
 
     device = mm_port_probe_peek_device (probe);
-    udevDevice = mm_port_probe_peek_port (probe);
+    port_device = mm_port_probe_peek_port (probe);
 
     ctx = g_slice_new (TelitCustomInitContext);
     ctx->result = g_simple_async_result_new (G_OBJECT (probe),
@@ -308,7 +308,7 @@ telit_custom_init (MMPortProbe *probe,
     ctx->getportcfg_retries = 3;
 
     /* If the device is tagged for supporting #PORTCFG do the custom init */
-    if (g_udev_device_get_property_as_boolean (udevDevice, "ID_MM_TELIT_PORTS_TAGGED")) {
+    if (mm_kernel_device_get_property_as_boolean (port_device, "ID_MM_TELIT_PORTS_TAGGED")) {
         telit_custom_init_step (ctx);
         return;
     }
diff --git a/plugins/x22x/mm-plugin-x22x.c b/plugins/x22x/mm-plugin-x22x.c
index 8e21075..a137273 100644
--- a/plugins/x22x/mm-plugin-x22x.c
+++ b/plugins/x22x/mm-plugin-x22x.c
@@ -220,7 +220,7 @@ grab_port (MMPlugin *self,
            MMPortProbe *probe,
            GError **error)
 {
-    GUdevDevice *port;
+    MMKernelDevice *port;
     MMPortType ptype;
     MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
 
@@ -232,12 +232,12 @@ grab_port (MMPlugin *self,
          * be the data/primary port on these devices.  We have to tag them based on
          * what the Windows .INF files say the port layout should be.
          */
-        if (g_udev_device_get_property_as_boolean (port, "ID_MM_X22X_PORT_TYPE_MODEM")) {
+        if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_X22X_PORT_TYPE_MODEM")) {
             mm_dbg ("x22x: AT port '%s/%s' flagged as primary",
                     mm_port_probe_get_port_subsys (probe),
                     mm_port_probe_get_port_name (probe));
             pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
-        } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_X22X_PORT_TYPE_AUX")) {
+        } else if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_X22X_PORT_TYPE_AUX")) {
             mm_dbg ("x22x: AT port '%s/%s' flagged as secondary",
                     mm_port_probe_get_port_subsys (probe),
                     mm_port_probe_get_port_name (probe));
diff --git a/plugins/zte/mm-plugin-zte.c b/plugins/zte/mm-plugin-zte.c
index 239ee6b..09eb27d 100644
--- a/plugins/zte/mm-plugin-zte.c
+++ b/plugins/zte/mm-plugin-zte.c
@@ -110,7 +110,7 @@ grab_port (MMPlugin *self,
            MMPortProbe *probe,
            GError **error)
 {
-    GUdevDevice *port;
+    MMKernelDevice *port;
     MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
     MMPortType ptype;
 
@@ -128,12 +128,12 @@ grab_port (MMPlugin *self,
 
     if (mm_port_probe_is_at (probe)) {
         /* Look for port type hints */
-        if (g_udev_device_get_property_as_boolean (port, "ID_MM_ZTE_PORT_TYPE_MODEM")) {
+        if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_ZTE_PORT_TYPE_MODEM")) {
             mm_dbg ("ZTE: AT port '%s/%s' flagged as primary",
                     mm_port_probe_get_port_subsys (probe),
                     mm_port_probe_get_port_name (probe));
             pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
-        } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_ZTE_PORT_TYPE_AUX")) {
+        } else if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_ZTE_PORT_TYPE_AUX")) {
             mm_dbg ("ZTE: AT port '%s/%s' flagged as secondary",
                     mm_port_probe_get_port_subsys (probe),
                     mm_port_probe_get_port_name (probe));
@@ -141,7 +141,7 @@ grab_port (MMPlugin *self,
         }
     }
 
-    if (g_udev_device_get_property_as_boolean (port, "ID_MM_ZTE_ICERA_DHCP")) {
+    if (mm_kernel_device_get_property_as_boolean (port, "ID_MM_ZTE_ICERA_DHCP")) {
         mm_dbg ("ZTE: Icera-based modem will use DHCP");
         g_object_set (modem,
                       MM_BROADBAND_MODEM_ICERA_DEFAULT_IP_METHOD, MM_BEARER_IP_METHOD_DHCP,
diff --git a/src/Makefile.am b/src/Makefile.am
index 0ac8382..911b802 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -22,6 +22,7 @@ AM_CFLAGS = \
 	-I$(top_srcdir)/libmm-glib \
 	-I${top_builddir}/libmm-glib/generated \
 	-I${top_builddir}/libmm-glib/generated/tests \
+	-I$(srcdir)/kerneldevice \
 	$(NULL)
 
 AM_LDFLAGS = \
@@ -166,6 +167,19 @@ BUILT_SOURCES += $(PORT_ENUMS_GENERATED)
 CLEANFILES    += $(PORT_ENUMS_GENERATED)
 
 ################################################################################
+# kerneldevice library
+################################################################################
+
+noinst_LTLIBRARIES += libkerneldevice.la
+
+libkerneldevice_la_SOURCES = \
+	kerneldevice/mm-kernel-device.h \
+	kerneldevice/mm-kernel-device.c \
+	kerneldevice/mm-kernel-device-udev.h \
+	kerneldevice/mm-kernel-device-udev.c \
+	$(NULL)
+
+################################################################################
 # ModemManager daemon
 ################################################################################
 
@@ -208,6 +222,7 @@ ModemManager_LDADD = \
 	$(top_builddir)/libmm-glib/generated/tests/libmm-test-generated.la \
 	$(builddir)/libhelpers.la \
 	$(builddir)/libport.la \
+	$(builddir)/libkerneldevice.la \
 	$(NULL)
 
 ModemManager_SOURCES = \
diff --git a/src/kerneldevice/mm-kernel-device-udev.c b/src/kerneldevice/mm-kernel-device-udev.c
new file mode 100644
index 0000000..1870206
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-udev.c
@@ -0,0 +1,614 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ *
+ * Copyright (C) 2016 Velocloud, Inc.
+ */
+
+#include <string.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-kernel-device-udev.h"
+#include "mm-log.h"
+
+G_DEFINE_TYPE (MMKernelDeviceUdev, mm_kernel_device_udev,  MM_TYPE_KERNEL_DEVICE)
+
+enum {
+    PROP_0,
+    PROP_UDEV_DEVICE,
+    PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _MMKernelDeviceUdevPrivate {
+    GUdevDevice *device;
+    GUdevDevice *parent;
+    GUdevDevice *physdev;
+    guint16      vendor;
+    guint16      product;
+};
+
+/*****************************************************************************/
+
+static gboolean
+get_device_ids (GUdevDevice *device,
+                guint16     *vendor,
+                guint16     *product)
+{
+    GUdevDevice *parent = NULL;
+    const gchar *vid = NULL, *pid = NULL, *parent_subsys;
+    gboolean success = FALSE;
+    char *pci_vid = NULL, *pci_pid = NULL;
+
+    parent = g_udev_device_get_parent (device);
+    if (parent) {
+        parent_subsys = g_udev_device_get_subsystem (parent);
+        if (parent_subsys) {
+            if (g_str_equal (parent_subsys, "bluetooth")) {
+                /* Bluetooth devices report the VID/PID of the BT adapter here,
+                 * which isn't really what we want.  Just return null IDs instead.
+                 */
+                success = TRUE;
+                goto out;
+            } else if (g_str_equal (parent_subsys, "pcmcia")) {
+                /* For PCMCIA devices we need to grab the PCMCIA subsystem's
+                 * manfid and cardid, since any IDs on the tty device itself
+                 * may be from PCMCIA controller or something else.
+                 */
+                vid = g_udev_device_get_sysfs_attr (parent, "manf_id");
+                pid = g_udev_device_get_sysfs_attr (parent, "card_id");
+                if (!vid || !pid)
+                    goto out;
+            } else if (g_str_equal (parent_subsys, "platform")) {
+                /* Platform devices don't usually have a VID/PID */
+                success = TRUE;
+                goto out;
+            } else if (g_str_has_prefix (parent_subsys, "usb") &&
+                       (!g_strcmp0 (g_udev_device_get_driver (parent), "qmi_wwan") ||
+                        !g_strcmp0 (g_udev_device_get_driver (parent), "cdc_mbim"))) {
+                /* Need to look for vendor/product in the parent of the QMI/MBIM device */
+                GUdevDevice *qmi_parent;
+
+                qmi_parent = g_udev_device_get_parent (parent);
+                if (qmi_parent) {
+                    vid = g_udev_device_get_property (qmi_parent, "ID_VENDOR_ID");
+                    pid = g_udev_device_get_property (qmi_parent, "ID_MODEL_ID");
+                    g_object_unref (qmi_parent);
+                }
+            } else if (g_str_equal (parent_subsys, "pci")) {
+                const char *pci_id;
+
+                /* We can't always rely on the model + vendor showing up on
+                 * the PCI device's child, so look at the PCI parent.  PCI_ID
+                 * has the format "1931:000C".
+                 */
+                pci_id = g_udev_device_get_property (parent, "PCI_ID");
+                if (pci_id && strlen (pci_id) == 9 && pci_id[4] == ':') {
+                    vid = pci_vid = g_strdup (pci_id);
+                    pci_vid[4] = '\0';
+                    pid = pci_pid = g_strdup (pci_id + 5);
+                }
+            }
+        }
+    }
+
+    if (!vid)
+        vid = g_udev_device_get_property (device, "ID_VENDOR_ID");
+    if (!vid)
+        goto out;
+
+    if (strncmp (vid, "0x", 2) == 0)
+        vid += 2;
+    if (strlen (vid) != 4)
+        goto out;
+
+    if (vendor) {
+        *vendor = (guint16) (mm_utils_hex2byte (vid + 2) & 0xFF);
+        *vendor |= (guint16) ((mm_utils_hex2byte (vid) & 0xFF) << 8);
+    }
+
+    if (!pid)
+        pid = g_udev_device_get_property (device, "ID_MODEL_ID");
+    if (!pid) {
+        *vendor = 0;
+        goto out;
+    }
+
+    if (strncmp (pid, "0x", 2) == 0)
+        pid += 2;
+    if (strlen (pid) != 4) {
+        *vendor = 0;
+        goto out;
+    }
+
+    if (product) {
+        *product = (guint16) (mm_utils_hex2byte (pid + 2) & 0xFF);
+        *product |= (guint16) ((mm_utils_hex2byte (pid) & 0xFF) << 8);
+    }
+
+    success = TRUE;
+
+out:
+    if (parent)
+        g_object_unref (parent);
+    g_free (pci_vid);
+    g_free (pci_pid);
+    return success;
+}
+
+static void
+ensure_device_ids (MMKernelDeviceUdev *self)
+{
+    if (self->priv->vendor || self->priv->product)
+        return;
+
+    if (!get_device_ids (self->priv->device, &self->priv->vendor, &self->priv->product))
+        mm_dbg ("(%s/%s) could not get vendor/product ID",
+                g_udev_device_get_subsystem (self->priv->device),
+                g_udev_device_get_name      (self->priv->device));
+}
+
+/*****************************************************************************/
+
+static GUdevDevice *
+find_physical_gudevdevice (GUdevDevice *child)
+{
+    GUdevDevice *iter, *old = NULL;
+    GUdevDevice *physdev = NULL;
+    const char *subsys, *type, *name;
+    guint32 i = 0;
+    gboolean is_usb = FALSE, is_pci = FALSE, is_pcmcia = FALSE, is_platform = FALSE;
+    gboolean is_pnp = FALSE;
+
+    g_return_val_if_fail (child != NULL, NULL);
+
+    /* Bluetooth rfcomm devices are "virtual" and don't necessarily have
+     * parents at all.
+     */
+    name = g_udev_device_get_name (child);
+    if (name && strncmp (name, "rfcomm", 6) == 0)
+        return g_object_ref (child);
+
+    iter = g_object_ref (child);
+    while (iter && i++ < 8) {
+        subsys = g_udev_device_get_subsystem (iter);
+        if (subsys) {
+            if (is_usb || g_str_has_prefix (subsys, "usb")) {
+                is_usb = TRUE;
+                type = g_udev_device_get_devtype (iter);
+                if (type && !strcmp (type, "usb_device")) {
+                    physdev = iter;
+                    break;
+                }
+            } else if (is_pcmcia || !strcmp (subsys, "pcmcia")) {
+                GUdevDevice *pcmcia_parent;
+                const char *tmp_subsys;
+
+                is_pcmcia = TRUE;
+
+                /* If the parent of this PCMCIA device is no longer part of
+                 * the PCMCIA subsystem, we want to stop since we're looking
+                 * for the base PCMCIA device, not the PCMCIA controller which
+                 * is usually PCI or some other bus type.
+                 */
+                pcmcia_parent = g_udev_device_get_parent (iter);
+                if (pcmcia_parent) {
+                    tmp_subsys = g_udev_device_get_subsystem (pcmcia_parent);
+                    if (tmp_subsys && strcmp (tmp_subsys, "pcmcia"))
+                        physdev = iter;
+                    g_object_unref (pcmcia_parent);
+                    if (physdev)
+                        break;
+                }
+            } else if (is_platform || !strcmp (subsys, "platform")) {
+                /* Take the first platform parent as the physical device */
+                is_platform = TRUE;
+                physdev = iter;
+                break;
+            } else if (is_pci || !strcmp (subsys, "pci")) {
+                is_pci = TRUE;
+                physdev = iter;
+                break;
+            } else if (is_pnp || !strcmp (subsys, "pnp")) {
+                is_pnp = TRUE;
+                physdev = iter;
+                break;
+            }
+        }
+
+        old = iter;
+        iter = g_udev_device_get_parent (old);
+        g_object_unref (old);
+    }
+
+    return physdev;
+}
+
+static void
+ensure_physdev (MMKernelDeviceUdev *self)
+{
+    if (self->priv->physdev)
+        return;
+    self->priv->physdev = find_physical_gudevdevice (self->priv->device);
+}
+
+/*****************************************************************************/
+
+static const gchar *
+kernel_device_get_subsystem (MMKernelDevice *self)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (self), NULL);
+
+    return g_udev_device_get_subsystem (MM_KERNEL_DEVICE_UDEV (self)->priv->device);
+}
+
+static const gchar *
+kernel_device_get_name (MMKernelDevice *self)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (self), NULL);
+
+    return g_udev_device_get_name (MM_KERNEL_DEVICE_UDEV (self)->priv->device);
+}
+
+static const gchar *
+kernel_device_get_driver (MMKernelDevice *_self)
+{
+    MMKernelDeviceUdev *self;
+    const gchar *driver, *subsys, *name;
+
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), NULL);
+
+    self = MM_KERNEL_DEVICE_UDEV (_self);
+
+    driver = g_udev_device_get_driver (self->priv->device);
+    if (!driver) {
+        GUdevDevice *parent;
+
+        parent = g_udev_device_get_parent (self->priv->device);
+        if (parent)
+            driver = g_udev_device_get_driver (parent);
+
+        /* Check for bluetooth; it's driver is a bunch of levels up so we
+         * just check for the subsystem of the parent being bluetooth.
+         */
+        if (!driver && parent) {
+            subsys = g_udev_device_get_subsystem (parent);
+            if (subsys && !strcmp (subsys, "bluetooth"))
+                driver = "bluetooth";
+        }
+
+        if (parent)
+            g_object_unref (parent);
+    }
+
+    /* Newer kernels don't set up the rfcomm port parent in sysfs,
+     * so we must infer it from the device name.
+     */
+    name = g_udev_device_get_name (self->priv->device);
+    if (!driver && strncmp (name, "rfcomm", 6) == 0)
+        driver = "bluetooth";
+
+    /* Note: may return NULL! */
+    return driver;
+}
+
+static const gchar *
+kernel_device_get_sysfs_path (MMKernelDevice *self)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), NULL);
+
+    return g_udev_device_get_sysfs_path (MM_KERNEL_DEVICE_UDEV (self)->priv->device);
+}
+
+static const gchar *
+kernel_device_get_physdev_uid (MMKernelDevice *_self)
+{
+    MMKernelDeviceUdev *self;
+
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), NULL);
+
+    self = MM_KERNEL_DEVICE_UDEV (_self);
+    ensure_physdev (self);
+    return (self->priv->physdev ? g_udev_device_get_sysfs_path (self->priv->physdev) : NULL);
+}
+
+static guint16
+kernel_device_get_physdev_vid (MMKernelDevice *_self)
+{
+    MMKernelDeviceUdev *self;
+
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), 0);
+
+    self = MM_KERNEL_DEVICE_UDEV (_self);
+    ensure_device_ids (self);
+    return self->priv->vendor;
+}
+
+static guint16
+kernel_device_get_physdev_pid (MMKernelDevice *_self)
+{
+    MMKernelDeviceUdev *self;
+
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), 0);
+
+    self = MM_KERNEL_DEVICE_UDEV (_self);
+    ensure_device_ids (self);
+    return self->priv->product;
+}
+
+static const gchar *
+kernel_device_get_parent_sysfs_path (MMKernelDevice *_self)
+{
+    MMKernelDeviceUdev *self;
+
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), 0);
+
+    self = MM_KERNEL_DEVICE_UDEV (_self);
+    if (!self->priv->parent)
+        self->priv->parent = g_udev_device_get_parent (self->priv->device);
+    return (self->priv->parent ? g_udev_device_get_sysfs_path (self->priv->parent) : NULL);
+}
+
+static gboolean
+kernel_device_is_candidate (MMKernelDevice *_self,
+                            gboolean        manual_scan)
+{
+    MMKernelDeviceUdev *self;
+    const gchar *physdev_subsys;
+    const gchar *name;
+    const gchar *subsys;
+
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), FALSE);
+
+    self = MM_KERNEL_DEVICE_UDEV (_self);
+
+    name   = g_udev_device_get_name      (self->priv->device);
+    subsys = g_udev_device_get_subsystem (self->priv->device);
+
+    /* ignore VTs */
+    if (strncmp (name, "tty", 3) == 0 && g_ascii_isdigit (name[3]))
+        return FALSE;
+
+    /* Ignore devices that aren't completely configured by udev yet.  If
+     * ModemManager is started in parallel with udev, explicitly requesting
+     * devices may return devices for which not all udev rules have yet been
+     * applied (a bug in udev/gudev).  Since we often need those rules to match
+     * the device to a specific ModemManager driver, we need to ensure that all
+     * rules have been processed before handling a device.
+     *
+     * The udev tag applies to each port in a device. In other words, the flag
+     * may be set in some ports, but not in others */
+    if (!g_udev_device_get_property_as_boolean (self->priv->device, "ID_MM_CANDIDATE"))
+        return FALSE;
+
+    /* Load physical device. If there is no physical device, we don't process
+     * the device. */
+    ensure_physdev (self);
+    if (!self->priv->physdev) {
+        /* Log about it, but filter out some common ports that we know don't have
+         * anything to do with mobile broadband.
+         */
+        if (   strcmp (name, "console")
+            && strcmp (name, "ptmx")
+            && strcmp (name, "lo")
+            && strcmp (name, "tty")
+            && !strstr (name, "virbr"))
+            mm_dbg ("(%s/%s): could not get port's parent device", subsys, name);
+        return FALSE;
+    }
+
+    /* The blacklist applies to the device as a whole, and therefore the flag
+     * will be applied always in the physical device, not in each port. */
+    if (g_udev_device_get_property_as_boolean (self->priv->physdev, "ID_MM_DEVICE_IGNORE")) {
+        mm_dbg ("(%s/%s): device is blacklisted", subsys, name);
+        return FALSE;
+    }
+
+    /* Is the device in the manual-only greylist? If so, return if this is an
+     * automatic scan. */
+    if (!manual_scan && g_udev_device_get_property_as_boolean (self->priv->physdev, "ID_MM_DEVICE_MANUAL_SCAN_ONLY")) {
+        mm_dbg ("(%s/%s): device probed only in manual scan", subsys, name);
+        return FALSE;
+    }
+
+    /* If the physdev is a 'platform' or 'pnp' device that's not whitelisted, ignore it */
+    physdev_subsys = g_udev_device_get_subsystem (self->priv->physdev);
+    if ((!g_strcmp0 (physdev_subsys, "platform") || !g_strcmp0 (physdev_subsys, "pnp")) &&
+        (!g_udev_device_get_property_as_boolean (self->priv->physdev, "ID_MM_PLATFORM_DRIVER_PROBE"))) {
+        mm_dbg ("(%s/%s): port's parent platform driver is not whitelisted", subsys, name);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+kernel_device_cmp (MMKernelDevice *_a,
+                   MMKernelDevice *_b)
+{
+    MMKernelDeviceUdev *a;
+    MMKernelDeviceUdev *b;
+
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_a), FALSE);
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_b), FALSE);
+
+    a = MM_KERNEL_DEVICE_UDEV (_a);
+    b = MM_KERNEL_DEVICE_UDEV (_b);
+
+    if (g_udev_device_has_property (a->priv->device, "DEVPATH_OLD") &&
+        g_str_has_suffix (g_udev_device_get_sysfs_path (b->priv->device),
+                          g_udev_device_get_property   (a->priv->device, "DEVPATH_OLD")))
+        return TRUE;
+
+    if (g_udev_device_has_property (b->priv->device, "DEVPATH_OLD") &&
+        g_str_has_suffix (g_udev_device_get_sysfs_path (a->priv->device),
+                          g_udev_device_get_property   (b->priv->device, "DEVPATH_OLD")))
+        return TRUE;
+
+    return !g_strcmp0 (g_udev_device_get_sysfs_path (a->priv->device), g_udev_device_get_sysfs_path (b->priv->device));
+}
+
+static gboolean
+kernel_device_has_property (MMKernelDevice *_self,
+                            const gchar    *property)
+{
+    MMKernelDeviceUdev *self;
+
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), FALSE);
+
+    self = MM_KERNEL_DEVICE_UDEV (_self);
+    return g_udev_device_has_property (self->priv->device, property);
+}
+
+static const gchar *
+kernel_device_get_property (MMKernelDevice *_self,
+                            const gchar    *property)
+{
+    MMKernelDeviceUdev *self;
+
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), NULL);
+
+    self = MM_KERNEL_DEVICE_UDEV (_self);
+    return g_udev_device_get_property (self->priv->device, property);
+}
+
+static gboolean
+kernel_device_get_property_as_boolean (MMKernelDevice *_self,
+                                       const gchar    *property)
+{
+    MMKernelDeviceUdev *self;
+
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), FALSE);
+
+    self = MM_KERNEL_DEVICE_UDEV (_self);
+    return g_udev_device_get_property_as_boolean (self->priv->device, property);
+}
+
+static gint
+kernel_device_get_property_as_int (MMKernelDevice *_self,
+                                   const gchar    *property)
+{
+    MMKernelDeviceUdev *self;
+
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), -1);
+
+    self = MM_KERNEL_DEVICE_UDEV (_self);
+    return g_udev_device_get_property_as_int (self->priv->device, property);
+}
+
+/*****************************************************************************/
+
+MMKernelDevice *
+mm_kernel_device_udev_new (GUdevDevice *udev_device)
+{
+    g_return_val_if_fail (G_UDEV_IS_DEVICE (udev_device), NULL);
+
+    return MM_KERNEL_DEVICE (g_object_new (MM_TYPE_KERNEL_DEVICE_UDEV,
+                                           "udev-device", udev_device,
+                                           NULL));
+}
+
+/*****************************************************************************/
+
+static void
+mm_kernel_device_udev_init (MMKernelDeviceUdev *self)
+{
+    /* Initialize private data */
+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdevPrivate);
+}
+
+static void
+set_property (GObject      *object,
+              guint         prop_id,
+              const GValue *value,
+              GParamSpec   *pspec)
+{
+    MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object);
+
+    switch (prop_id) {
+    case PROP_UDEV_DEVICE:
+        g_assert (!self->priv->device);
+        self->priv->device = g_value_dup_object (value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+get_property (GObject    *object,
+              guint       prop_id,
+              GValue     *value,
+              GParamSpec *pspec)
+{
+    MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object);
+
+    switch (prop_id) {
+    case PROP_UDEV_DEVICE:
+        g_value_set_object (value, self->priv->device);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+dispose (GObject *object)
+{
+    MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object);
+
+    g_clear_object (&self->priv->physdev);
+    g_clear_object (&self->priv->parent);
+    g_clear_object (&self->priv->device);
+
+    G_OBJECT_CLASS (mm_kernel_device_udev_parent_class)->dispose (object);
+}
+
+static void
+mm_kernel_device_udev_class_init (MMKernelDeviceUdevClass *klass)
+{
+    GObjectClass        *object_class        = G_OBJECT_CLASS (klass);
+    MMKernelDeviceClass *kernel_device_class = MM_KERNEL_DEVICE_CLASS (klass);
+
+    g_type_class_add_private (object_class, sizeof (MMKernelDeviceUdevPrivate));
+
+    object_class->dispose      = dispose;
+    object_class->get_property = get_property;
+    object_class->set_property = set_property;
+
+    kernel_device_class->get_subsystem           = kernel_device_get_subsystem;
+    kernel_device_class->get_name                = kernel_device_get_name;
+    kernel_device_class->get_driver              = kernel_device_get_driver;
+    kernel_device_class->get_sysfs_path          = kernel_device_get_sysfs_path;
+    kernel_device_class->get_physdev_uid         = kernel_device_get_physdev_uid;
+    kernel_device_class->get_physdev_vid         = kernel_device_get_physdev_vid;
+    kernel_device_class->get_physdev_pid         = kernel_device_get_physdev_pid;
+    kernel_device_class->get_parent_sysfs_path   = kernel_device_get_parent_sysfs_path;
+    kernel_device_class->is_candidate            = kernel_device_is_candidate;
+    kernel_device_class->cmp                     = kernel_device_cmp;
+    kernel_device_class->has_property            = kernel_device_has_property;
+    kernel_device_class->get_property            = kernel_device_get_property;
+    kernel_device_class->get_property_as_boolean = kernel_device_get_property_as_boolean;
+    kernel_device_class->get_property_as_int     = kernel_device_get_property_as_int;
+
+    properties[PROP_UDEV_DEVICE] =
+        g_param_spec_object ("udev-device",
+                             "udev device",
+                             "Device object as reported by GUdev",
+                             G_UDEV_TYPE_DEVICE,
+                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+    g_object_class_install_property (object_class, PROP_UDEV_DEVICE, properties[PROP_UDEV_DEVICE]);
+}
diff --git a/src/kerneldevice/mm-kernel-device-udev.h b/src/kerneldevice/mm-kernel-device-udev.h
new file mode 100644
index 0000000..ed83159
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-udev.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ *
+ * Copyright (C) 2016 Velocloud, Inc.
+ */
+
+#ifndef MM_KERNEL_DEVICE_UDEV_H
+#define MM_KERNEL_DEVICE_UDEV_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gudev/gudev.h>
+
+#include "mm-kernel-device.h"
+
+#define MM_TYPE_KERNEL_DEVICE_UDEV            (mm_kernel_device_udev_get_type ())
+#define MM_KERNEL_DEVICE_UDEV(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdev))
+#define MM_KERNEL_DEVICE_UDEV_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdevClass))
+#define MM_IS_KERNEL_DEVICE_UDEV(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_DEVICE_UDEV))
+#define MM_IS_KERNEL_DEVICE_UDEV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_KERNEL_DEVICE_UDEV))
+#define MM_KERNEL_DEVICE_UDEV_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdevClass))
+
+typedef struct _MMKernelDeviceUdev        MMKernelDeviceUdev;
+typedef struct _MMKernelDeviceUdevClass   MMKernelDeviceUdevClass;
+typedef struct _MMKernelDeviceUdevPrivate MMKernelDeviceUdevPrivate;
+
+struct _MMKernelDeviceUdev {
+    MMKernelDevice parent;
+    MMKernelDeviceUdevPrivate *priv;
+};
+
+struct _MMKernelDeviceUdevClass {
+    MMKernelDeviceClass parent;
+};
+
+GType           mm_kernel_device_udev_get_type (void);
+MMKernelDevice *mm_kernel_device_udev_new      (GUdevDevice *udev_device);
+
+#endif /* MM_KERNEL_DEVICE_UDEV_H */
diff --git a/src/kerneldevice/mm-kernel-device.c b/src/kerneldevice/mm-kernel-device.c
new file mode 100644
index 0000000..ee25fe9
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device.c
@@ -0,0 +1,179 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ *
+ * Copyright (C) 2016 Velocloud, Inc.
+ */
+
+#include "mm-kernel-device.h"
+
+G_DEFINE_ABSTRACT_TYPE (MMKernelDevice, mm_kernel_device, G_TYPE_OBJECT)
+
+/*****************************************************************************/
+
+const gchar *
+mm_kernel_device_get_subsystem (MMKernelDevice *self)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), NULL);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_subsystem ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->get_subsystem (self) :
+            NULL);
+}
+
+const gchar *
+mm_kernel_device_get_name (MMKernelDevice *self)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), NULL);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_name ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->get_name (self) :
+            NULL);
+}
+
+const gchar *
+mm_kernel_device_get_driver (MMKernelDevice *self)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), NULL);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_driver ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->get_driver (self) :
+            NULL);
+}
+
+const gchar *
+mm_kernel_device_get_sysfs_path (MMKernelDevice *self)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), NULL);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_sysfs_path ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->get_sysfs_path (self) :
+            NULL);
+}
+
+const gchar *
+mm_kernel_device_get_physdev_uid (MMKernelDevice *self)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), NULL);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_uid ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_uid (self) :
+            NULL);
+}
+
+guint16
+mm_kernel_device_get_physdev_vid (MMKernelDevice *self)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), 0);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_vid ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_vid (self) :
+            0);
+}
+
+guint16
+mm_kernel_device_get_physdev_pid (MMKernelDevice *self)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), 0);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_pid ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_pid (self) :
+            0);
+}
+
+gboolean
+mm_kernel_device_is_candidate (MMKernelDevice *self,
+                               gboolean        manual_scan)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), FALSE);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->is_candidate ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->is_candidate (self, manual_scan) :
+            FALSE);
+}
+
+const gchar *
+mm_kernel_device_get_parent_sysfs_path (MMKernelDevice *self)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), NULL);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_parent_sysfs_path ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->get_parent_sysfs_path (self) :
+            NULL);
+}
+
+gboolean
+mm_kernel_device_cmp (MMKernelDevice *a,
+                      MMKernelDevice *b)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (a), FALSE);
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (b), FALSE);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (a)->cmp ?
+            MM_KERNEL_DEVICE_GET_CLASS (a)->cmp (a, b) :
+            FALSE);
+}
+
+gboolean
+mm_kernel_device_has_property (MMKernelDevice *self,
+                               const gchar    *property)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), FALSE);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->has_property ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->has_property (self, property) :
+            FALSE);
+}
+
+const gchar *
+mm_kernel_device_get_property (MMKernelDevice *self,
+                               const gchar    *property)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), NULL);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_property ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->get_property (self, property) :
+            NULL);
+}
+
+gboolean
+mm_kernel_device_get_property_as_boolean (MMKernelDevice *self,
+                                          const gchar    *property)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), FALSE);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_property_as_boolean ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->get_property_as_boolean (self, property) :
+            FALSE);
+}
+
+gint
+mm_kernel_device_get_property_as_int (MMKernelDevice *self,
+                                      const gchar    *property)
+{
+    g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), -1);
+
+    return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_property_as_int ?
+            MM_KERNEL_DEVICE_GET_CLASS (self)->get_property_as_int (self, property) :
+            -1);
+}
+
+/*****************************************************************************/
+
+static void
+mm_kernel_device_init (MMKernelDevice *self)
+{
+}
+
+static void
+mm_kernel_device_class_init (MMKernelDeviceClass *klass)
+{
+}
diff --git a/src/kerneldevice/mm-kernel-device.h b/src/kerneldevice/mm-kernel-device.h
new file mode 100644
index 0000000..11e1111
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ *
+ * Copyright (C) 2016 Velocloud, Inc.
+ */
+
+#ifndef MM_KERNEL_DEVICE_H
+#define MM_KERNEL_DEVICE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define MM_TYPE_KERNEL_DEVICE            (mm_kernel_device_get_type ())
+#define MM_KERNEL_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_DEVICE, MMKernelDevice))
+#define MM_KERNEL_DEVICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_KERNEL_DEVICE, MMKernelDeviceClass))
+#define MM_IS_KERNEL_DEVICE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_DEVICE))
+#define MM_IS_KERNEL_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_KERNEL_DEVICE))
+#define MM_KERNEL_DEVICE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_KERNEL_DEVICE, MMKernelDeviceClass))
+
+typedef struct _MMKernelDevice MMKernelDevice;
+typedef struct _MMKernelDeviceClass MMKernelDeviceClass;
+
+struct _MMKernelDevice {
+    GObject parent;
+};
+
+struct _MMKernelDeviceClass {
+    GObjectClass parent;
+
+    const gchar * (* get_subsystem)   (MMKernelDevice *self);
+
+    const gchar * (* get_name)        (MMKernelDevice *self);
+
+    const gchar * (* get_driver)      (MMKernelDevice *self);
+
+    const gchar * (* get_sysfs_path)  (MMKernelDevice *self);
+
+    gboolean      (* is_candidate)    (MMKernelDevice *self,
+                                       gboolean        manual_scan);
+
+    const gchar * (* get_parent_sysfs_path) (MMKernelDevice *self);
+
+    const gchar * (* get_physdev_uid) (MMKernelDevice *self);
+
+    guint16       (* get_physdev_vid) (MMKernelDevice *self);
+
+    guint16       (* get_physdev_pid) (MMKernelDevice *self);
+
+    gboolean      (* cmp) (MMKernelDevice *a, MMKernelDevice *b);
+
+    gboolean      (* has_property)            (MMKernelDevice *self, const gchar *property);
+    const gchar * (* get_property)            (MMKernelDevice *self, const gchar *property);
+    gboolean      (* get_property_as_boolean) (MMKernelDevice *self, const gchar *property);
+    gint          (* get_property_as_int)     (MMKernelDevice *self, const gchar *property);
+};
+
+GType mm_kernel_device_get_type (void);
+
+const gchar *mm_kernel_device_get_subsystem   (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_name        (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_driver      (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_sysfs_path  (MMKernelDevice *self);
+
+gboolean     mm_kernel_device_is_candidate    (MMKernelDevice *self,
+                                               gboolean        manual_scan);
+
+const gchar *mm_kernel_device_get_parent_sysfs_path  (MMKernelDevice *self);
+
+const gchar *mm_kernel_device_get_physdev_uid (MMKernelDevice *self);
+guint16      mm_kernel_device_get_physdev_vid (MMKernelDevice *self);
+guint16      mm_kernel_device_get_physdev_pid (MMKernelDevice *self);
+
+gboolean     mm_kernel_device_cmp (MMKernelDevice *a, MMKernelDevice *b);
+
+gboolean     mm_kernel_device_has_property            (MMKernelDevice *self, const gchar *property);
+const gchar *mm_kernel_device_get_property            (MMKernelDevice *self, const gchar *property);
+gboolean     mm_kernel_device_get_property_as_boolean (MMKernelDevice *self, const gchar *property);
+gint         mm_kernel_device_get_property_as_int     (MMKernelDevice *self, const gchar *property);
+
+#endif /* MM_KERNEL_DEVICE_H */
diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c
index cdc5d54..9da3896 100644
--- a/src/mm-base-manager.c
+++ b/src/mm-base-manager.c
@@ -12,15 +12,16 @@
  *
  * Copyright (C) 2008 - 2009 Novell, Inc.
  * Copyright (C) 2009 - 2012 Red Hat, Inc.
- * Copyright (C) 2011 - 2012 Aleksander Morgado <aleksander at gnu.org>
- * Copyright (C) 2011 - 2012 Google, Inc.
+ * Copyright (C) 2011 - 2012 Google, Inc
+ * Copyright (C) 2016 Velocloud, Inc.
+ * Copyright (C) 2011 - 2016 Aleksander Morgado <aleksander at aleksander.es>
  */
 
 #include <string.h>
 #include <ctype.h>
 
 #include <gmodule.h>
-#include <gudev/gudev.h>
+#include "mm-kernel-device-udev.h"
 
 #include <ModemManager.h>
 #include <mm-errors-types.h>
@@ -94,8 +95,8 @@ find_device_by_modem (MMBaseManager *manager,
 }
 
 static MMDevice *
-find_device_by_port (MMBaseManager *manager,
-                     GUdevDevice *port)
+find_device_by_port (MMBaseManager  *manager,
+                     MMKernelDevice *port)
 {
     GHashTableIter iter;
     gpointer key, value;
@@ -118,10 +119,10 @@ find_device_by_physdev_uid (MMBaseManager *self,
 }
 
 static MMDevice *
-find_device_by_udev_device (MMBaseManager *manager,
-                            GUdevDevice *udev_device)
+find_device_by_kernel_device (MMBaseManager  *manager,
+                              MMKernelDevice *kernel_device)
 {
-    return find_device_by_physdev_uid (manager, g_udev_device_get_sysfs_path (udev_device));
+    return find_device_by_physdev_uid (manager, mm_kernel_device_get_physdev_uid (kernel_device));
 }
 
 /*****************************************************************************/
@@ -175,100 +176,26 @@ device_support_check_ready (MMPluginManager          *plugin_manager,
     find_device_support_context_free (ctx);
 }
 
-static GUdevDevice *
-find_physical_device (GUdevDevice *child)
-{
-    GUdevDevice *iter, *old = NULL;
-    GUdevDevice *physdev = NULL;
-    const char *subsys, *type, *name;
-    guint32 i = 0;
-    gboolean is_usb = FALSE, is_pci = FALSE, is_pcmcia = FALSE, is_platform = FALSE;
-    gboolean is_pnp = FALSE;
-
-    g_return_val_if_fail (child != NULL, NULL);
-
-    /* Bluetooth rfcomm devices are "virtual" and don't necessarily have
-     * parents at all.
-     */
-    name = g_udev_device_get_name (child);
-    if (name && strncmp (name, "rfcomm", 6) == 0)
-        return g_object_ref (child);
-
-    iter = g_object_ref (child);
-    while (iter && i++ < 8) {
-        subsys = g_udev_device_get_subsystem (iter);
-        if (subsys) {
-            if (is_usb || g_str_has_prefix (subsys, "usb")) {
-                is_usb = TRUE;
-                type = g_udev_device_get_devtype (iter);
-                if (type && !strcmp (type, "usb_device")) {
-                    physdev = iter;
-                    break;
-                }
-            } else if (is_pcmcia || !strcmp (subsys, "pcmcia")) {
-                GUdevDevice *pcmcia_parent;
-                const char *tmp_subsys;
-
-                is_pcmcia = TRUE;
-
-                /* If the parent of this PCMCIA device is no longer part of
-                 * the PCMCIA subsystem, we want to stop since we're looking
-                 * for the base PCMCIA device, not the PCMCIA controller which
-                 * is usually PCI or some other bus type.
-                 */
-                pcmcia_parent = g_udev_device_get_parent (iter);
-                if (pcmcia_parent) {
-                    tmp_subsys = g_udev_device_get_subsystem (pcmcia_parent);
-                    if (tmp_subsys && strcmp (tmp_subsys, "pcmcia"))
-                        physdev = iter;
-                    g_object_unref (pcmcia_parent);
-                    if (physdev)
-                        break;
-                }
-            } else if (is_platform || !strcmp (subsys, "platform")) {
-                /* Take the first platform parent as the physical device */
-                is_platform = TRUE;
-                physdev = iter;
-                break;
-            } else if (is_pci || !strcmp (subsys, "pci")) {
-                is_pci = TRUE;
-                physdev = iter;
-                break;
-            } else if (is_pnp || !strcmp (subsys, "pnp")) {
-                is_pnp = TRUE;
-                physdev = iter;
-                break;
-            }
-        }
-
-        old = iter;
-        iter = g_udev_device_get_parent (old);
-        g_object_unref (old);
-    }
-
-    return physdev;
-}
-
 static void
-device_removed (MMBaseManager *self,
-                GUdevDevice *udev_device)
+device_removed (MMBaseManager  *self,
+                MMKernelDevice *kernel_device)
 {
     MMDevice *device;
     const gchar *subsys;
     const gchar *name;
 
-    g_return_if_fail (udev_device != NULL);
+    g_return_if_fail (kernel_device != NULL);
 
-    subsys = g_udev_device_get_subsystem (udev_device);
-    name = g_udev_device_get_name (udev_device);
+    subsys = mm_kernel_device_get_subsystem (kernel_device);
+    name = mm_kernel_device_get_name (kernel_device);
 
     if (!g_str_has_prefix (subsys, "usb") ||
         (name && g_str_has_prefix (name, "cdc-wdm"))) {
         /* Handle tty/net/wdm port removal */
-        device = find_device_by_port (self, udev_device);
+        device = find_device_by_port (self, kernel_device);
         if (device) {
             mm_info ("(%s/%s): released by device '%s'", subsys, name, mm_device_get_uid (device));
-            mm_device_release_port (device, udev_device);
+            mm_device_release_port (device, kernel_device);
 
             /* If port probe list gets empty, remove the device object iself */
             if (!mm_device_peek_port_probe_list (device)) {
@@ -291,105 +218,49 @@ device_removed (MMBaseManager *self,
      * device has been removed.  That way, if the device is reinserted later, we'll go through
      * the process of exporting it.
      */
-    device = find_device_by_udev_device (self, udev_device);
+    device = find_device_by_kernel_device (self, kernel_device);
     if (device) {
         mm_dbg ("Removing device '%s'", mm_device_get_uid (device));
         mm_device_remove_modem (device);
         g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
         return;
     }
-
-    /* Maybe a plugin is checking whether or not the port is supported.
-     * TODO: Cancel every possible supports check in this port. */
 }
 
 static void
-device_added (MMBaseManager *manager,
-              GUdevDevice *port,
-              gboolean hotplugged,
-              gboolean manual_scan)
+device_added (MMBaseManager  *manager,
+              MMKernelDevice *port,
+              gboolean        hotplugged,
+              gboolean        manual_scan)
 {
-    MMDevice *device;
-    const char *subsys, *name, *physdev_uid, *physdev_subsys;
-    gboolean is_candidate;
-    GUdevDevice *physdev = NULL;
+    MMDevice    *device;
+    const gchar *physdev_uid;
 
     g_return_if_fail (port != NULL);
 
-    subsys = g_udev_device_get_subsystem (port);
-    name = g_udev_device_get_name (port);
-
-    /* ignore VTs */
-    if (strncmp (name, "tty", 3) == 0 && isdigit (name[3]))
-        return;
-
-    /* Ignore devices that aren't completely configured by udev yet.  If
-     * ModemManager is started in parallel with udev, explicitly requesting
-     * devices may return devices for which not all udev rules have yet been
-     * applied (a bug in udev/gudev).  Since we often need those rules to match
-     * the device to a specific ModemManager driver, we need to ensure that all
-     * rules have been processed before handling a device.
-     */
-    is_candidate = g_udev_device_get_property_as_boolean (port, "ID_MM_CANDIDATE");
-    if (!is_candidate) {
-        /* This could mean that device changed, loosing its ID_MM_CANDIDATE
+    if (!mm_kernel_device_is_candidate (port, manual_scan)) {
+        /* This could mean that device changed, losing its ID_MM_CANDIDATE
          * flags (such as Bluetooth RFCOMM devices upon disconnect.
          * Try to forget it. */
-        if (hotplugged && !manual_scan)
-            device_removed (manager, port);
+        device_removed (manager, port);
+        mm_dbg ("(%s/%s): port not candidate",
+                mm_kernel_device_get_subsystem (port),
+                mm_kernel_device_get_name (port));
         return;
     }
 
-    if (find_device_by_port (manager, port))
+    /* If already added, ignore new event */
+    if (find_device_by_port (manager, port)) {
+        mm_dbg ("(%s/%s): port already added",
+                mm_kernel_device_get_subsystem (port),
+                mm_kernel_device_get_name (port));
         return;
-
-    /* Find the port's physical device's sysfs path.  This is the kernel device
-     * that "owns" all the ports of the device, like the USB device or the PCI
-     * device the provides each tty or network port.
-     */
-    physdev = find_physical_device (port);
-    if (!physdev) {
-        /* Warn about it, but filter out some common ports that we know don't have
-         * anything to do with mobile broadband.
-         */
-        if (   strcmp (name, "console")
-            && strcmp (name, "ptmx")
-            && strcmp (name, "lo")
-            && strcmp (name, "tty")
-            && !strstr (name, "virbr"))
-            mm_dbg ("(%s/%s): could not get port's parent device", subsys, name);
-
-        goto out;
-    }
-
-    /* Is the device blacklisted? */
-    if (g_udev_device_get_property_as_boolean (physdev, "ID_MM_DEVICE_IGNORE")) {
-        mm_dbg ("(%s/%s): port's parent device is blacklisted", subsys, name);
-        goto out;
     }
 
-    /* Is the device in the manual-only greylist? If so, return if this is an
-     * automatic scan. */
-    if (!manual_scan && g_udev_device_get_property_as_boolean (physdev, "ID_MM_DEVICE_MANUAL_SCAN_ONLY")) {
-        mm_dbg ("(%s/%s): port probed only in manual scan", subsys, name);
-        goto out;
-    }
-
-    /* If the physdev is a 'platform' or 'pnp' device that's not whitelisted, ignore it */
-    physdev_subsys = g_udev_device_get_subsystem (physdev);
-    if (   physdev_subsys
-        && (   g_str_equal (physdev_subsys, "platform")
-            || g_str_equal (physdev_subsys, "pnp"))
-        && !g_udev_device_get_property_as_boolean (physdev, "ID_MM_PLATFORM_DRIVER_PROBE")) {
-        mm_dbg ("(%s/%s): port's parent platform driver is not whitelisted", subsys, name);
-        goto out;
-    }
-
-    physdev_uid = g_udev_device_get_sysfs_path (physdev);
-    if (!physdev_uid) {
-        mm_dbg ("(%s/%s): could not get port's parent device sysfs path", subsys, name);
-        goto out;
-    }
+    /* Get the port's physical device's uid. All ports of the same physical
+     * device will share the same uid. */
+    physdev_uid = mm_kernel_device_get_physdev_uid (port);
+    g_assert (physdev_uid);
 
     /* See if we already created an object to handle ports in this device */
     device = find_device_by_physdev_uid (manager, physdev_uid);
@@ -397,7 +268,7 @@ device_added (MMBaseManager *manager,
         FindDeviceSupportContext *ctx;
 
         /* Keep the device listed in the Manager */
-        device = mm_device_new (physdev, hotplugged);
+        device = mm_device_new (physdev_uid, hotplugged, FALSE);
         g_hash_table_insert (manager->priv->devices,
                              g_strdup (physdev_uid),
                              device);
@@ -415,10 +286,6 @@ device_added (MMBaseManager *manager,
 
     /* Grab the port in the existing device. */
     mm_device_grab_port (device, port);
-
-out:
-    if (physdev)
-        g_object_unref (physdev);
 }
 
 static void
@@ -430,6 +297,7 @@ handle_uevent (GUdevClient *client,
     MMBaseManager *self = MM_BASE_MANAGER (user_data);
     const gchar *subsys;
     const gchar *name;
+    MMKernelDevice *kernel_device;
 
     g_return_if_fail (action != NULL);
 
@@ -438,15 +306,19 @@ handle_uevent (GUdevClient *client,
     g_return_if_fail (subsys != NULL);
     g_return_if_fail (g_str_equal (subsys, "tty") || g_str_equal (subsys, "net") || g_str_has_prefix (subsys, "usb"));
 
+    kernel_device = mm_kernel_device_udev_new (device);
+
     /* We only care about tty/net and usb/cdc-wdm devices when adding modem ports,
      * but for remove, also handle usb parent device remove events
      */
-    name = g_udev_device_get_name (device);
+    name = mm_kernel_device_get_name (kernel_device);
     if (   (g_str_equal (action, "add") || g_str_equal (action, "move") || g_str_equal (action, "change"))
         && (!g_str_has_prefix (subsys, "usb") || (name && g_str_has_prefix (name, "cdc-wdm"))))
-        device_added (self, device, TRUE, FALSE);
+        device_added (self, kernel_device, TRUE, FALSE);
     else if (g_str_equal (action, "remove"))
-        device_removed (self, device);
+        device_removed (self, kernel_device);
+
+    g_object_unref (kernel_device);
 }
 
 typedef struct {
@@ -458,7 +330,12 @@ typedef struct {
 static gboolean
 start_device_added_idle (StartDeviceAdded *ctx)
 {
-    device_added (ctx->self, ctx->device, FALSE, ctx->manual_scan);
+    MMKernelDevice *kernel_device;
+
+    kernel_device = mm_kernel_device_udev_new (ctx->device);
+    device_added (ctx->self, kernel_device, FALSE, ctx->manual_scan);
+    g_object_unref (kernel_device);
+
     g_object_unref (ctx->self);
     g_object_unref (ctx->device);
     g_slice_free (StartDeviceAdded, ctx);
@@ -757,7 +634,7 @@ handle_set_profile (MmGdbusTest *skeleton,
 
     /* Create device and keep it listed in the Manager */
     physdev_uid = g_strdup_printf ("/virtual/%s", id);
-    device = mm_device_virtual_new (physdev_uid, TRUE);
+    device = mm_device_new (physdev_uid, TRUE, TRUE);
     g_hash_table_insert (self->priv->devices, physdev_uid, device);
 
     /* Grab virtual ports */
diff --git a/src/mm-device.c b/src/mm-device.c
index 6ab90b2..ffa4fd2 100644
--- a/src/mm-device.c
+++ b/src/mm-device.c
@@ -32,7 +32,6 @@ G_DEFINE_TYPE (MMDevice, mm_device, G_TYPE_OBJECT);
 enum {
     PROP_0,
     PROP_UID,
-    PROP_UDEV_DEVICE,
     PROP_PLUGIN,
     PROP_MODEM,
     PROP_HOTPLUGGED,
@@ -56,8 +55,7 @@ struct _MMDevicePrivate {
     /* Unique id */
     gchar *uid;
 
-    /* Parent UDev device */
-    GUdevDevice *udev_device;
+    /* If USB, device vid/pid */
     guint16 vendor;
     guint16 product;
 
@@ -87,22 +85,16 @@ struct _MMDevicePrivate {
 /*****************************************************************************/
 
 static MMPortProbe *
-device_find_probe_with_device (MMDevice    *self,
-                               GUdevDevice *udev_port,
-                               gboolean lookup_ignored)
+device_find_probe_with_device (MMDevice       *self,
+                               MMKernelDevice *kernel_port,
+                               gboolean        lookup_ignored)
 {
     GList *l;
 
     for (l = self->priv->port_probes; l; l = g_list_next (l)) {
         MMPortProbe *probe = MM_PORT_PROBE (l->data);
 
-        if (   g_udev_device_has_property (udev_port, "DEVPATH_OLD")
-            && g_str_has_suffix (g_udev_device_get_sysfs_path (mm_port_probe_peek_port (probe)),
-                                 g_udev_device_get_property (udev_port, "DEVPATH_OLD")))
-            return probe;
-
-        if (g_str_equal (g_udev_device_get_sysfs_path (mm_port_probe_peek_port (probe)),
-                         g_udev_device_get_sysfs_path (udev_port)))
+        if (mm_kernel_device_cmp (mm_port_probe_peek_port (probe), kernel_port))
             return probe;
     }
 
@@ -112,13 +104,7 @@ device_find_probe_with_device (MMDevice    *self,
     for (l = self->priv->ignored_port_probes; l; l = g_list_next (l)) {
         MMPortProbe *probe = MM_PORT_PROBE (l->data);
 
-        if (   g_udev_device_has_property (udev_port, "DEVPATH_OLD")
-            && g_str_has_suffix (g_udev_device_get_sysfs_path (mm_port_probe_peek_port (probe)),
-                                 g_udev_device_get_property (udev_port, "DEVPATH_OLD")))
-            return probe;
-
-        if (g_str_equal (g_udev_device_get_sysfs_path (mm_port_probe_peek_port (probe)),
-                         g_udev_device_get_sysfs_path (udev_port)))
+        if (mm_kernel_device_cmp (mm_port_probe_peek_port (probe), kernel_port))
             return probe;
     }
 
@@ -126,164 +112,21 @@ device_find_probe_with_device (MMDevice    *self,
 }
 
 gboolean
-mm_device_owns_port (MMDevice    *self,
-                     GUdevDevice *udev_port)
-{
-    return !!device_find_probe_with_device (self, udev_port, TRUE);
-}
-
-static gboolean
-get_device_ids (GUdevDevice *device,
-                guint16     *vendor,
-                guint16     *product)
-{
-    GUdevDevice *parent = NULL;
-    const gchar *vid = NULL, *pid = NULL, *parent_subsys;
-    gboolean success = FALSE;
-    char *pci_vid = NULL, *pci_pid = NULL;
-
-    parent = g_udev_device_get_parent (device);
-    if (parent) {
-        parent_subsys = g_udev_device_get_subsystem (parent);
-        if (parent_subsys) {
-            if (g_str_equal (parent_subsys, "bluetooth")) {
-                /* Bluetooth devices report the VID/PID of the BT adapter here,
-                 * which isn't really what we want.  Just return null IDs instead.
-                 */
-                success = TRUE;
-                goto out;
-            } else if (g_str_equal (parent_subsys, "pcmcia")) {
-                /* For PCMCIA devices we need to grab the PCMCIA subsystem's
-                 * manfid and cardid, since any IDs on the tty device itself
-                 * may be from PCMCIA controller or something else.
-                 */
-                vid = g_udev_device_get_sysfs_attr (parent, "manf_id");
-                pid = g_udev_device_get_sysfs_attr (parent, "card_id");
-                if (!vid || !pid)
-                    goto out;
-            } else if (g_str_equal (parent_subsys, "platform")) {
-                /* Platform devices don't usually have a VID/PID */
-                success = TRUE;
-                goto out;
-            } else if (g_str_has_prefix (parent_subsys, "usb") &&
-                       (!g_strcmp0 (g_udev_device_get_driver (parent), "qmi_wwan") ||
-                        !g_strcmp0 (g_udev_device_get_driver (parent), "cdc_mbim"))) {
-                /* Need to look for vendor/product in the parent of the QMI/MBIM device */
-                GUdevDevice *qmi_parent;
-
-                qmi_parent = g_udev_device_get_parent (parent);
-                if (qmi_parent) {
-                    vid = g_udev_device_get_property (qmi_parent, "ID_VENDOR_ID");
-                    pid = g_udev_device_get_property (qmi_parent, "ID_MODEL_ID");
-                    g_object_unref (qmi_parent);
-                }
-            } else if (g_str_equal (parent_subsys, "pci")) {
-                const char *pci_id;
-
-                /* We can't always rely on the model + vendor showing up on
-                 * the PCI device's child, so look at the PCI parent.  PCI_ID
-                 * has the format "1931:000C".
-                 */
-                pci_id = g_udev_device_get_property (parent, "PCI_ID");
-                if (pci_id && strlen (pci_id) == 9 && pci_id[4] == ':') {
-                    vid = pci_vid = g_strdup (pci_id);
-                    pci_vid[4] = '\0';
-                    pid = pci_pid = g_strdup (pci_id + 5);
-                }
-            }
-        }
-    }
-
-    if (!vid)
-        vid = g_udev_device_get_property (device, "ID_VENDOR_ID");
-    if (!vid)
-        goto out;
-
-    if (strncmp (vid, "0x", 2) == 0)
-        vid += 2;
-    if (strlen (vid) != 4)
-        goto out;
-
-    if (vendor) {
-        *vendor = (guint16) (mm_utils_hex2byte (vid + 2) & 0xFF);
-        *vendor |= (guint16) ((mm_utils_hex2byte (vid) & 0xFF) << 8);
-    }
-
-    if (!pid)
-        pid = g_udev_device_get_property (device, "ID_MODEL_ID");
-    if (!pid) {
-        *vendor = 0;
-        goto out;
-    }
-
-    if (strncmp (pid, "0x", 2) == 0)
-        pid += 2;
-    if (strlen (pid) != 4) {
-        *vendor = 0;
-        goto out;
-    }
-
-    if (product) {
-        *product = (guint16) (mm_utils_hex2byte (pid + 2) & 0xFF);
-        *product |= (guint16) ((mm_utils_hex2byte (pid) & 0xFF) << 8);
-    }
-
-    success = TRUE;
-
-out:
-    if (parent)
-        g_object_unref (parent);
-    g_free (pci_vid);
-    g_free (pci_pid);
-    return success;
-}
-
-const gchar *
-mm_device_utils_get_port_driver (GUdevDevice *udev_port)
-{
-    const gchar *driver, *subsys;
-    const char *name = g_udev_device_get_name (udev_port);
-
-    driver = g_udev_device_get_driver (udev_port);
-    if (!driver) {
-        GUdevDevice *parent;
-
-        parent = g_udev_device_get_parent (udev_port);
-        if (parent)
-            driver = g_udev_device_get_driver (parent);
-
-        /* Check for bluetooth; it's driver is a bunch of levels up so we
-         * just check for the subsystem of the parent being bluetooth.
-         */
-        if (!driver && parent) {
-            subsys = g_udev_device_get_subsystem (parent);
-            if (subsys && !strcmp (subsys, "bluetooth"))
-                driver = "bluetooth";
-        }
-
-        if (parent)
-            g_object_unref (parent);
-    }
-
-    /* Newer kernels don't set up the rfcomm port parent in sysfs,
-     * so we must infer it from the device name.
-     */
-    if (!driver && strncmp (name, "rfcomm", 6) == 0)
-        driver = "bluetooth";
-
-    /* Note: may return NULL! */
-    return driver;
+mm_device_owns_port (MMDevice       *self,
+                     MMKernelDevice *kernel_port)
+{
+    return !!device_find_probe_with_device (self, kernel_port, TRUE);
 }
 
 static void
-add_port_driver (MMDevice *self,
-                 GUdevDevice *udev_port)
+add_port_driver (MMDevice       *self,
+                 MMKernelDevice *kernel_port)
 {
     const gchar *driver;
     guint n_items;
     guint i;
 
-    driver = mm_device_utils_get_port_driver (udev_port);
+    driver = mm_kernel_device_get_driver (kernel_port);
     if (!driver)
         return;
 
@@ -301,51 +144,46 @@ add_port_driver (MMDevice *self,
     if (!driver)
         return;
 
-    self->priv->drivers = g_realloc (self->priv->drivers,
-                                     (n_items + 2) * sizeof (gchar *));
+    self->priv->drivers = g_realloc (self->priv->drivers, (n_items + 2) * sizeof (gchar *));
     self->priv->drivers[n_items] = g_strdup (driver);
     self->priv->drivers[n_items + 1] = NULL;
 }
 
 void
-mm_device_grab_port (MMDevice    *self,
-                     GUdevDevice *udev_port)
+mm_device_grab_port (MMDevice       *self,
+                     MMKernelDevice *kernel_port)
 {
     MMPortProbe *probe;
 
-    if (mm_device_owns_port (self, udev_port))
+    if (mm_device_owns_port (self, kernel_port))
         return;
 
     /* Get the vendor/product IDs out of the first one that gives us
      * some valid value (it seems we may get NULL reported for VID in QMI
      * ports, e.g. Huawei E367) */
     if (!self->priv->vendor && !self->priv->product) {
-        if (!get_device_ids (udev_port,
-                             &self->priv->vendor,
-                             &self->priv->product)) {
-            mm_dbg ("(%s) could not get vendor/product ID",
-                    self->priv->uid);
-        }
+        self->priv->vendor  = mm_kernel_device_get_physdev_vid (kernel_port);
+        self->priv->product = mm_kernel_device_get_physdev_pid (kernel_port);
     }
 
     /* Add new port driver */
-    add_port_driver (self, udev_port);
+    add_port_driver (self, kernel_port);
 
     /* Create and store new port probe */
-    probe = mm_port_probe_new (self, udev_port);
+    probe = mm_port_probe_new (self, kernel_port);
     self->priv->port_probes = g_list_prepend (self->priv->port_probes, probe);
 
     /* Notify about the grabbed port */
-    g_signal_emit (self, signals[SIGNAL_PORT_GRABBED], 0, udev_port);
+    g_signal_emit (self, signals[SIGNAL_PORT_GRABBED], 0, kernel_port);
 }
 
 void
-mm_device_release_port (MMDevice    *self,
-                        GUdevDevice *udev_port)
+mm_device_release_port (MMDevice       *self,
+                        MMKernelDevice *kernel_port)
 {
     MMPortProbe *probe;
 
-    probe = device_find_probe_with_device (self, udev_port, TRUE);
+    probe = device_find_probe_with_device (self, kernel_port, TRUE);
     if (probe) {
         /* Found, remove from lists and destroy probe */
         if (g_list_find (self->priv->port_probes, probe))
@@ -360,17 +198,18 @@ mm_device_release_port (MMDevice    *self,
 }
 
 void
-mm_device_ignore_port  (MMDevice *self,
-                        GUdevDevice *udev_port)
+mm_device_ignore_port  (MMDevice       *self,
+                        MMKernelDevice *kernel_port)
 {
     MMPortProbe *probe;
 
-    probe = device_find_probe_with_device (self, udev_port, FALSE);
+    probe = device_find_probe_with_device (self, kernel_port, FALSE);
     if (probe) {
         /* Found, remove from list and add to the ignored list */
-        mm_dbg ("Fully ignoring port '%s/%s' from now on",
-                g_udev_device_get_subsystem (udev_port),
-                g_udev_device_get_name (udev_port));
+        mm_dbg ("[device %s] fully ignoring port '%s/%s' from now on",
+                self->priv->uid,
+                mm_kernel_device_get_subsystem (kernel_port),
+                mm_kernel_device_get_name (kernel_port));
         self->priv->port_probes = g_list_remove (self->priv->port_probes, probe);
         self->priv->ignored_port_probes = g_list_prepend (self->priv->ignored_port_probes, probe);
     }
@@ -392,10 +231,7 @@ unexport_modem (MMDevice *self)
         g_object_set (self->priv->modem,
                       MM_BASE_MODEM_CONNECTION, NULL,
                       NULL);
-
-        mm_dbg ("Unexported modem '%s' from path '%s'",
-                g_udev_device_get_sysfs_path (self->priv->udev_device),
-                path);
+        mm_dbg ("[device %s] unexported modem from path '%s'", self->priv->uid, path);
         g_free (path);
     }
 }
@@ -414,8 +250,7 @@ export_modem (MMDevice *self)
 
     /* If modem not yet valid (not fully initialized), don't export it */
     if (!mm_base_modem_get_valid (self->priv->modem)) {
-        mm_dbg ("Modem '%s' not yet fully initialized",
-                g_udev_device_get_sysfs_path (self->priv->udev_device));
+        mm_dbg ("[device %s] modem not yet fully initialized", self->priv->uid);
         return;
     }
 
@@ -425,8 +260,7 @@ export_modem (MMDevice *self)
                   NULL);
     if (path) {
         g_free (path);
-        mm_dbg ("Modem '%s' already exported",
-                g_udev_device_get_sysfs_path (self->priv->udev_device));
+        mm_dbg ("[device %s] modem already exported", self->priv->uid);
         return;
     }
 
@@ -445,21 +279,14 @@ export_modem (MMDevice *self)
     g_dbus_object_manager_server_export (self->priv->object_manager,
                                          G_DBUS_OBJECT_SKELETON (self->priv->modem));
 
-    mm_dbg ("Exported modem '%s' at path '%s'",
-            (self->priv->virtual ?
-             self->priv->uid :
-             g_udev_device_get_sysfs_path (self->priv->udev_device)),
-            path);
-
-    /* Once connected, dump additional debug info about the modem */
-    mm_dbg ("(%s): '%s' modem, VID 0x%04X PID 0x%04X (%s)",
-            path,
-            mm_base_modem_get_plugin (self->priv->modem),
+    mm_dbg ("[device %s] exported modem at path '%s'", self->priv->uid, path);
+    mm_dbg ("[device %s]    plugin:  %s", self->priv->uid, mm_base_modem_get_plugin (self->priv->modem));
+    mm_dbg ("[device %s]    vid:pid: 0x%04X:0x%04X",
+            self->priv->uid,
             (mm_base_modem_get_vendor_id (self->priv->modem) & 0xFFFF),
-            (mm_base_modem_get_product_id (self->priv->modem) & 0xFFFF),
-            (self->priv->virtual ?
-             "virtual" :
-             g_udev_device_get_subsystem (self->priv->udev_device)));
+            (mm_base_modem_get_product_id (self->priv->modem) & 0xFFFF));
+    if (self->priv->virtual)
+        mm_dbg ("[device %s]    virtual", self->priv->uid);
 
     g_free (path);
 }
@@ -499,7 +326,7 @@ modem_valid (MMBaseModem *modem,
         if (self->priv->modem)
             export_modem (self);
         else
-            mm_dbg ("Not exporting modem; no longer available");
+            mm_dbg ("[device %s] not exporting modem; no longer available", self->priv->uid);
     }
 }
 
@@ -520,7 +347,8 @@ mm_device_create_modem (MMDevice                  *self,
             return FALSE;
         }
 
-        mm_info ("Creating modem with plugin '%s' and '%u' ports",
+        mm_info ("[device %s] creating modem with plugin '%s' and '%u' ports",
+                 self->priv->uid,
                  mm_plugin_get_name (self->priv->plugin),
                  g_list_length (self->priv->port_probes));
     } else {
@@ -532,7 +360,8 @@ mm_device_create_modem (MMDevice                  *self,
             return FALSE;
         }
 
-        mm_info ("Creating virtual modem with plugin '%s' and '%u' ports",
+        mm_info ("[device %s] creating virtual modem with plugin '%s' and '%u' ports",
+                 self->priv->uid,
                  mm_plugin_get_name (self->priv->plugin),
                  g_strv_length (self->priv->virtual_ports));
     }
@@ -578,20 +407,6 @@ mm_device_get_product (MMDevice *self)
     return self->priv->product;
 }
 
-GUdevDevice *
-mm_device_peek_udev_device (MMDevice *self)
-{
-    g_return_val_if_fail (self->priv->udev_device != NULL, NULL);
-    return self->priv->udev_device;
-}
-
-GUdevDevice *
-mm_device_get_udev_device (MMDevice *self)
-{
-    g_return_val_if_fail (self->priv->udev_device != NULL, NULL);
-    return G_UDEV_DEVICE (g_object_ref (self->priv->udev_device));
-}
-
 void
 mm_device_set_plugin (MMDevice *self,
                       GObject  *plugin)
@@ -634,22 +449,22 @@ mm_device_get_modem (MMDevice *self)
 }
 
 GObject *
-mm_device_peek_port_probe (MMDevice *self,
-                           GUdevDevice *udev_port)
+mm_device_peek_port_probe (MMDevice       *self,
+                           MMKernelDevice *kernel_port)
 {
     MMPortProbe *probe;
 
-    probe = device_find_probe_with_device (self, udev_port, FALSE);
+    probe = device_find_probe_with_device (self, kernel_port, FALSE);
     return (probe ? G_OBJECT (probe) : NULL);
 }
 
 GObject *
-mm_device_get_port_probe (MMDevice *self,
-                          GUdevDevice *udev_port)
+mm_device_get_port_probe (MMDevice       *self,
+                          MMKernelDevice *kernel_port)
 {
     MMPortProbe *probe;
 
-    probe = device_find_probe_with_device (self, udev_port, FALSE);
+    probe = device_find_probe_with_device (self, kernel_port, FALSE);
     return (probe ? g_object_ref (probe) : NULL);
 }
 
@@ -710,28 +525,16 @@ mm_device_is_virtual (MMDevice *self)
 /*****************************************************************************/
 
 MMDevice *
-mm_device_new (GUdevDevice *udev_device,
-               gboolean hotplugged)
-{
-    g_return_val_if_fail (udev_device != NULL, NULL);
-
-    return MM_DEVICE (g_object_new (MM_TYPE_DEVICE,
-                                    MM_DEVICE_UDEV_DEVICE, udev_device,
-                                    MM_DEVICE_UID, g_udev_device_get_sysfs_path (udev_device),
-                                    MM_DEVICE_HOTPLUGGED, hotplugged,
-                                    NULL));
-}
-
-MMDevice *
-mm_device_virtual_new (const gchar *uid,
-                       gboolean hotplugged)
+mm_device_new (const gchar *uid,
+               gboolean     hotplugged,
+               gboolean     virtual)
 {
     g_return_val_if_fail (uid != NULL, NULL);
 
     return MM_DEVICE (g_object_new (MM_TYPE_DEVICE,
-                                    MM_DEVICE_UID, uid,
+                                    MM_DEVICE_UID,        uid,
                                     MM_DEVICE_HOTPLUGGED, hotplugged,
-                                    MM_DEVICE_VIRTUAL, TRUE,
+                                    MM_DEVICE_VIRTUAL,    virtual,
                                     NULL));
 }
 
@@ -739,9 +542,7 @@ static void
 mm_device_init (MMDevice *self)
 {
     /* Initialize private data */
-    self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
-                                              MM_TYPE_DEVICE,
-                                              MMDevicePrivate);
+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_DEVICE, MMDevicePrivate);
 }
 
 static void
@@ -757,10 +558,6 @@ set_property (GObject *object,
         /* construct only */
         self->priv->uid = g_value_dup_string (value);
         break;
-    case PROP_UDEV_DEVICE:
-        /* construct only */
-        self->priv->udev_device = g_value_dup_object (value);
-        break;
     case PROP_PLUGIN:
         g_clear_object (&(self->priv->plugin));
         self->priv->plugin = g_value_dup_object (value);
@@ -793,9 +590,6 @@ get_property (GObject *object,
     case PROP_UID:
         g_value_set_string (value, self->priv->uid);
         break;
-    case PROP_UDEV_DEVICE:
-        g_value_set_object (value, self->priv->udev_device);
-        break;
     case PROP_PLUGIN:
         g_value_set_object (value, self->priv->plugin);
         break;
@@ -819,7 +613,6 @@ dispose (GObject *object)
 {
     MMDevice *self = MM_DEVICE (object);
 
-    g_clear_object (&(self->priv->udev_device));
     g_clear_object (&(self->priv->plugin));
     g_list_free_full (self->priv->port_probes, (GDestroyNotify)g_object_unref);
     g_list_free_full (self->priv->ignored_port_probes, (GDestroyNotify)g_object_unref);
@@ -850,8 +643,8 @@ mm_device_class_init (MMDeviceClass *klass)
     /* Virtual methods */
     object_class->get_property = get_property;
     object_class->set_property = set_property;
-    object_class->finalize = finalize;
-    object_class->dispose = dispose;
+    object_class->finalize     = finalize;
+    object_class->dispose      = dispose;
 
     properties[PROP_UID] =
         g_param_spec_string (MM_DEVICE_UID,
@@ -861,14 +654,6 @@ mm_device_class_init (MMDeviceClass *klass)
                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
     g_object_class_install_property (object_class, PROP_UID, properties[PROP_UID]);
 
-    properties[PROP_UDEV_DEVICE] =
-        g_param_spec_object (MM_DEVICE_UDEV_DEVICE,
-                             "UDev Device",
-                             "UDev device object",
-                             G_UDEV_TYPE_DEVICE,
-                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-    g_object_class_install_property (object_class, PROP_UDEV_DEVICE, properties[PROP_UDEV_DEVICE]);
-
     properties[PROP_PLUGIN] =
         g_param_spec_object (MM_DEVICE_PLUGIN,
                              "Plugin",
@@ -908,7 +693,7 @@ mm_device_class_init (MMDeviceClass *klass)
                       G_STRUCT_OFFSET (MMDeviceClass, port_grabbed),
                       NULL, NULL,
                       g_cclosure_marshal_generic,
-                      G_TYPE_NONE, 1, G_UDEV_TYPE_DEVICE);
+                      G_TYPE_NONE, 1, MM_TYPE_KERNEL_DEVICE);
 
     signals[SIGNAL_PORT_RELEASED] =
         g_signal_new (MM_DEVICE_PORT_RELEASED,
@@ -917,5 +702,5 @@ mm_device_class_init (MMDeviceClass *klass)
                       G_STRUCT_OFFSET (MMDeviceClass, port_released),
                       NULL, NULL,
                       g_cclosure_marshal_generic,
-                      G_TYPE_NONE, 1, G_UDEV_TYPE_DEVICE);
+                      G_TYPE_NONE, 1, MM_TYPE_KERNEL_DEVICE);
 }
diff --git a/src/mm-device.h b/src/mm-device.h
index 57726e6..6e6ff0b 100644
--- a/src/mm-device.h
+++ b/src/mm-device.h
@@ -19,8 +19,7 @@
 #include <glib.h>
 #include <glib-object.h>
 
-#include <gudev/gudev.h>
-
+#include "mm-kernel-device.h"
 #include "mm-base-modem.h"
 
 #define MM_TYPE_DEVICE            (mm_device_get_type ())
@@ -34,12 +33,11 @@ typedef struct _MMDevice MMDevice;
 typedef struct _MMDeviceClass MMDeviceClass;
 typedef struct _MMDevicePrivate MMDevicePrivate;
 
-#define MM_DEVICE_UID         "uid"
-#define MM_DEVICE_UDEV_DEVICE "udev-device"
-#define MM_DEVICE_PLUGIN      "plugin"
-#define MM_DEVICE_MODEM       "modem"
-#define MM_DEVICE_HOTPLUGGED  "hotplugged"
-#define MM_DEVICE_VIRTUAL     "virtual"
+#define MM_DEVICE_UID        "uid"
+#define MM_DEVICE_PLUGIN     "plugin"
+#define MM_DEVICE_MODEM      "modem"
+#define MM_DEVICE_HOTPLUGGED "hotplugged"
+#define MM_DEVICE_VIRTUAL    "virtual"
 
 #define MM_DEVICE_PORT_GRABBED  "port-grabbed"
 #define MM_DEVICE_PORT_RELEASED "port-released"
@@ -53,62 +51,54 @@ struct _MMDeviceClass {
     GObjectClass parent;
 
     /* signals */
-    void (* port_grabbed)  (MMDevice *self,
-                            GUdevDevice *port);
-    void (* port_released) (MMDevice *self,
-                            GUdevDevice *port);
+    void (* port_grabbed)  (MMDevice       *self,
+                            MMKernelDevice *port);
+    void (* port_released) (MMDevice       *self,
+                            MMKernelDevice *port);
 };
 
 GType mm_device_get_type (void);
 
-MMDevice *mm_device_new (GUdevDevice *udev_device,
-                         gboolean hotplugged);
+MMDevice *mm_device_new (const gchar *uid,
+                         gboolean     hotplugged,
+                         gboolean     virtual);
 
-void     mm_device_grab_port    (MMDevice    *self,
-                                 GUdevDevice *udev_port);
-void     mm_device_release_port (MMDevice    *self,
-                                 GUdevDevice *udev_port);
-gboolean mm_device_owns_port    (MMDevice    *self,
-                                 GUdevDevice *udev_port);
-void     mm_device_ignore_port  (MMDevice *self,
-                                 GUdevDevice *udev_port);
+void     mm_device_grab_port    (MMDevice       *self,
+                                 MMKernelDevice *kernel_port);
+void     mm_device_release_port (MMDevice       *self,
+                                 MMKernelDevice *kernel_port);
+gboolean mm_device_owns_port    (MMDevice       *self,
+                                 MMKernelDevice *kernel_port);
+void     mm_device_ignore_port  (MMDevice       *self,
+                                 MMKernelDevice *kernel_port);
 
 gboolean mm_device_create_modem (MMDevice                  *self,
                                  GDBusObjectManagerServer  *object_manager,
                                  GError                   **error);
-void     mm_device_remove_modem (MMDevice  *self);
-
-const gchar  *mm_device_get_uid          (MMDevice *self);
-const gchar **mm_device_get_drivers      (MMDevice *self);
-guint16       mm_device_get_vendor       (MMDevice *self);
-guint16       mm_device_get_product      (MMDevice *self);
-GUdevDevice  *mm_device_peek_udev_device (MMDevice *self);
-GUdevDevice  *mm_device_get_udev_device  (MMDevice *self);
-void          mm_device_set_plugin       (MMDevice *self,
-                                          GObject  *plugin);
-GObject      *mm_device_peek_plugin      (MMDevice *self);
-GObject      *mm_device_get_plugin       (MMDevice *self);
-MMBaseModem  *mm_device_peek_modem       (MMDevice *self);
-MMBaseModem  *mm_device_get_modem        (MMDevice *self);
-
-GObject     *mm_device_peek_port_probe      (MMDevice *self,
-                                             GUdevDevice *udev_port);
-GObject     *mm_device_get_port_probe       (MMDevice *self,
-                                             GUdevDevice *udev_port);
-GList       *mm_device_peek_port_probe_list (MMDevice *self);
-GList       *mm_device_get_port_probe_list  (MMDevice *self);
-
-const gchar *mm_device_utils_get_port_driver (GUdevDevice *udev_port);
-
-gboolean    mm_device_get_hotplugged     (MMDevice *self);
-
+void     mm_device_remove_modem (MMDevice *self);
+
+const gchar     *mm_device_get_uid              (MMDevice       *self);
+const gchar    **mm_device_get_drivers          (MMDevice       *self);
+guint16          mm_device_get_vendor           (MMDevice       *self);
+guint16          mm_device_get_product          (MMDevice       *self);
+void             mm_device_set_plugin           (MMDevice       *self,
+                                                 GObject        *plugin);
+GObject         *mm_device_peek_plugin          (MMDevice       *self);
+GObject         *mm_device_get_plugin           (MMDevice       *self);
+MMBaseModem     *mm_device_peek_modem           (MMDevice       *self);
+MMBaseModem     *mm_device_get_modem            (MMDevice       *self);
+GObject         *mm_device_peek_port_probe      (MMDevice       *self,
+                                                 MMKernelDevice *kernel_port);
+GObject         *mm_device_get_port_probe       (MMDevice       *self,
+                                                 MMKernelDevice *kernel_port);
+GList           *mm_device_peek_port_probe_list (MMDevice       *self);
+GList           *mm_device_get_port_probe_list  (MMDevice       *self);
+gboolean         mm_device_get_hotplugged       (MMDevice       *self);
 
 /* For testing purposes */
-MMDevice     *mm_device_virtual_new        (const gchar *uid,
-                                            gboolean hotplugged);
-void          mm_device_virtual_grab_ports (MMDevice *self,
+void          mm_device_virtual_grab_ports (MMDevice     *self,
                                             const gchar **ports);
-const gchar **mm_device_virtual_peek_ports (MMDevice *self);
-gboolean      mm_device_is_virtual         (MMDevice *self);
+const gchar **mm_device_virtual_peek_ports (MMDevice     *self);
+gboolean      mm_device_is_virtual         (MMDevice     *self);
 
 #endif /* MM_DEVICE_H */
diff --git a/src/mm-plugin-manager.c b/src/mm-plugin-manager.c
index 8408dc3..058c5be 100644
--- a/src/mm-plugin-manager.c
+++ b/src/mm-plugin-manager.c
@@ -62,7 +62,7 @@ struct _MMPluginManagerPrivate {
 static GList *
 plugin_manager_build_plugins_list (MMPluginManager *self,
                                    MMDevice        *device,
-                                   GUdevDevice     *port)
+                                   MMKernelDevice  *port)
 {
     GList *list = NULL;
     GList *l;
@@ -179,8 +179,8 @@ struct _PortContext {
     gchar *name;
     /* The device where the port is*/
     MMDevice *device;
-    /* The GUDev reported port object */
-    GUdevDevice *port;
+    /* The reported kernel port object */
+    MMKernelDevice *port;
 
     /* The operation task */
     GTask *task;
@@ -642,7 +642,7 @@ static PortContext *
 port_context_new (MMPluginManager *self,
                   const gchar     *parent_name,
                   MMDevice        *device,
-                  GUdevDevice     *port)
+                  MMKernelDevice  *port)
 {
     PortContext *port_context;
 
@@ -653,7 +653,7 @@ port_context_new (MMPluginManager *self,
     port_context->timer     = g_timer_new ();
 
     /* Set context name */
-    port_context->name = g_strdup_printf ("%s,%s", parent_name, g_udev_device_get_name (port));
+    port_context->name = g_strdup_printf ("%s,%s", parent_name, mm_kernel_device_get_name (port));
 
     return port_context;
 }
@@ -757,8 +757,8 @@ device_context_ref (DeviceContext *device_context)
 }
 
 static PortContext *
-device_context_peek_running_port_context (DeviceContext *device_context,
-                                          GUdevDevice   *port)
+device_context_peek_running_port_context (DeviceContext  *device_context,
+                                          MMKernelDevice *port)
 {
     GList *l;
 
@@ -767,15 +767,15 @@ device_context_peek_running_port_context (DeviceContext *device_context,
 
         port_context = (PortContext *)(l->data);
         if ((port_context->port == port) ||
-            (!g_strcmp0 (g_udev_device_get_name (port_context->port), g_udev_device_get_name (port))))
+            (!g_strcmp0 (mm_kernel_device_get_name (port_context->port), mm_kernel_device_get_name (port))))
             return port_context;
     }
     return NULL;
 }
 
 static PortContext *
-device_context_peek_waiting_port_context (DeviceContext *device_context,
-                                          GUdevDevice   *port)
+device_context_peek_waiting_port_context (DeviceContext  *device_context,
+                                          MMKernelDevice *port)
 {
     GList *l;
 
@@ -784,7 +784,7 @@ device_context_peek_waiting_port_context (DeviceContext *device_context,
 
         port_context = (PortContext *)(l->data);
         if ((port_context->port == port) ||
-            (!g_strcmp0 (g_udev_device_get_name (port_context->port), g_udev_device_get_name (port))))
+            (!g_strcmp0 (mm_kernel_device_get_name (port_context->port), mm_kernel_device_get_name (port))))
             return port_context;
     }
     return NULL;
@@ -988,7 +988,7 @@ device_context_continue (DeviceContext *device_context)
         PortContext *port_context = (PortContext *) (l->data);
         const gchar *portname;
 
-        portname = g_udev_device_get_name (port_context->port);
+        portname = mm_kernel_device_get_name (port_context->port);
         if (!s)
             s = g_string_new (portname);
         else
@@ -1122,13 +1122,13 @@ device_context_min_wait_time_elapsed (DeviceContext *device_context)
 }
 
 static void
-device_context_port_released (DeviceContext *device_context,
-                              GUdevDevice   *port)
+device_context_port_released (DeviceContext  *device_context,
+                              MMKernelDevice *port)
 {
     PortContext *port_context;
 
     mm_dbg ("[plugin manager] task %s: port released: %s",
-            device_context->name, g_udev_device_get_name (port));
+            device_context->name, mm_kernel_device_get_name (port));
 
     /* Check if there's a waiting port context */
     port_context = device_context_peek_waiting_port_context (device_context, port);
@@ -1150,12 +1150,12 @@ device_context_port_released (DeviceContext *device_context,
     /* This is not something worth warning. If the probing task has already
      * been finished, it will already be removed from the list */
     mm_dbg ("[plugin manager] task %s: port wasn't found: %s",
-            device_context->name, g_udev_device_get_name (port));
+            device_context->name, mm_kernel_device_get_name (port));
 }
 
 static void
-device_context_port_grabbed (DeviceContext *device_context,
-                             GUdevDevice   *port)
+device_context_port_grabbed (DeviceContext  *device_context,
+                             MMKernelDevice *port)
 {
     MMPluginManager *self;
     PortContext     *port_context;
@@ -1164,7 +1164,7 @@ device_context_port_grabbed (DeviceContext *device_context,
     self = MM_PLUGIN_MANAGER (device_context->self);
 
     mm_dbg ("[plugin manager] task %s: port grabbed: %s",
-            device_context->name, g_udev_device_get_name (port));
+            device_context->name, mm_kernel_device_get_name (port));
 
     /* Ignore if for any reason we still have it in the running list */
     port_context = device_context_peek_running_port_context (device_context, port);
diff --git a/src/mm-plugin.c b/src/mm-plugin.c
index 0d9b408..bd5d313 100644
--- a/src/mm-plugin.c
+++ b/src/mm-plugin.c
@@ -23,13 +23,12 @@
 #include <unistd.h>
 #include <string.h>
 
-#include <gudev/gudev.h>
-
 #include <ModemManager.h>
 #include <mm-errors-types.h>
 
 #include "mm-plugin.h"
 #include "mm-device.h"
+#include "mm-kernel-device.h"
 #include "mm-port-serial-at.h"
 #include "mm-port-serial-qcdm.h"
 #include "mm-serial-parsers.h"
@@ -162,14 +161,14 @@ is_virtual_port (const gchar *device_name)
 
 /* Returns TRUE if the support check request was filtered out */
 static gboolean
-apply_subsystem_filter (MMPlugin *self,
-                        GUdevDevice *port)
+apply_subsystem_filter (MMPlugin       *self,
+                        MMKernelDevice *port)
 {
     if (self->priv->subsystems) {
         const gchar *subsys;
         guint i;
 
-        subsys = g_udev_device_get_subsystem (port);
+        subsys = mm_kernel_device_get_subsystem (port);
         for (i = 0; self->priv->subsystems[i]; i++) {
             if (g_str_equal (subsys, self->priv->subsystems[i]))
                 break;
@@ -189,11 +188,11 @@ apply_subsystem_filter (MMPlugin *self,
 
 /* Returns TRUE if the support check request was filtered out */
 static gboolean
-apply_pre_probing_filters (MMPlugin *self,
-                           MMDevice *device,
-                           GUdevDevice *port,
-                           gboolean *need_vendor_probing,
-                           gboolean *need_product_probing)
+apply_pre_probing_filters (MMPlugin       *self,
+                           MMDevice       *device,
+                           MMKernelDevice *port,
+                           gboolean       *need_vendor_probing,
+                           gboolean       *need_product_probing)
 {
     guint16 vendor;
     guint16 product;
@@ -209,7 +208,7 @@ apply_pre_probing_filters (MMPlugin *self,
     if (apply_subsystem_filter (self, port)) {
         mm_dbg ("(%s) [%s] filtered by subsystem",
                 self->priv->name,
-                g_udev_device_get_name (port));
+                mm_kernel_device_get_name (port));
         return TRUE;
     }
 
@@ -232,7 +231,7 @@ apply_pre_probing_filters (MMPlugin *self,
         const gchar **drivers;
 
         /* Detect any modems accessible through the list of virtual ports */
-        drivers = (is_virtual_port (g_udev_device_get_name (port)) ?
+        drivers = (is_virtual_port (mm_kernel_device_get_name (port)) ?
                    virtual_drivers :
                    mm_device_get_drivers (device));
 
@@ -240,7 +239,7 @@ apply_pre_probing_filters (MMPlugin *self,
         if (!drivers) {
             mm_dbg ("(%s) [%s] filtered as couldn't retrieve drivers",
                     self->priv->name,
-                    g_udev_device_get_name (port));
+                    mm_kernel_device_get_name (port));
             return TRUE;
         }
 
@@ -261,7 +260,7 @@ apply_pre_probing_filters (MMPlugin *self,
             if (!found) {
                 mm_dbg ("(%s) [%s] filtered by drivers",
                         self->priv->name,
-                        g_udev_device_get_name (port));
+                        mm_kernel_device_get_name (port));
                 return TRUE;
             }
         }
@@ -276,7 +275,7 @@ apply_pre_probing_filters (MMPlugin *self,
                     if (g_str_equal (drivers[j], self->priv->forbidden_drivers[i])) {
                         mm_dbg ("(%s) [%s] filtered by forbidden drivers",
                                 self->priv->name,
-                                g_udev_device_get_name (port));
+                                mm_kernel_device_get_name (port));
                         return TRUE;
                     }
                 }
@@ -292,7 +291,7 @@ apply_pre_probing_filters (MMPlugin *self,
                 if (g_str_equal (drivers[j], "qmi_wwan")) {
                     mm_dbg ("(%s) [%s] filtered by implicit QMI driver",
                             self->priv->name,
-                            g_udev_device_get_name (port));
+                            mm_kernel_device_get_name (port));
                     return TRUE;
                 }
             }
@@ -307,7 +306,7 @@ apply_pre_probing_filters (MMPlugin *self,
                 if (g_str_equal (drivers[j], "cdc_mbim")) {
                     mm_dbg ("(%s) [%s] filtered by implicit MBIM driver",
                             self->priv->name,
-                            g_udev_device_get_name (port));
+                            mm_kernel_device_get_name (port));
                     return TRUE;
                 }
             }
@@ -369,11 +368,11 @@ apply_pre_probing_filters (MMPlugin *self,
         ((!self->priv->vendor_strings &&
           !self->priv->product_strings &&
           !self->priv->forbidden_product_strings) ||
-         g_str_equal (g_udev_device_get_subsystem (port), "net") ||
-         g_str_has_prefix (g_udev_device_get_name (port), "cdc-wdm"))) {
+         g_str_equal (mm_kernel_device_get_subsystem (port), "net") ||
+         g_str_has_prefix (mm_kernel_device_get_name (port), "cdc-wdm"))) {
         mm_dbg ("(%s) [%s] filtered by vendor/product IDs",
                 self->priv->name,
-                g_udev_device_get_name (port));
+                mm_kernel_device_get_name (port));
         return TRUE;
     }
 
@@ -385,7 +384,7 @@ apply_pre_probing_filters (MMPlugin *self,
                 product == self->priv->forbidden_product_ids[i].r) {
                 mm_dbg ("(%s) [%s] filtered by forbidden vendor/product IDs",
                         self->priv->name,
-                        g_udev_device_get_name (port));
+                        mm_kernel_device_get_name (port));
                 return TRUE;
             }
         }
@@ -418,8 +417,7 @@ apply_pre_probing_filters (MMPlugin *self,
     if (self->priv->udev_tags) {
         for (i = 0; self->priv->udev_tags[i]; i++) {
             /* Check if the port was tagged */
-            if (g_udev_device_get_property_as_boolean (port,
-                                                       self->priv->udev_tags[i]))
+            if (mm_kernel_device_get_property_as_boolean (port, self->priv->udev_tags[i]))
                 break;
         }
 
@@ -427,7 +425,7 @@ apply_pre_probing_filters (MMPlugin *self,
         if (!self->priv->udev_tags[i]) {
             mm_dbg ("(%s) [%s] filtered by udev tags",
                     self->priv->name,
-                    g_udev_device_get_name (port));
+                    mm_kernel_device_get_name (port));
             return TRUE;
         }
     }
@@ -676,7 +674,7 @@ mm_plugin_supports_port_finish (MMPlugin      *self,
 void
 mm_plugin_supports_port (MMPlugin            *self,
                          MMDevice            *device,
-                         GUdevDevice         *port,
+                         MMKernelDevice      *port,
                          GCancellable        *cancellable,
                          GAsyncReadyCallback  callback,
                          gpointer             user_data)
@@ -691,7 +689,7 @@ mm_plugin_supports_port (MMPlugin            *self,
 
     g_return_if_fail (MM_IS_PLUGIN (self));
     g_return_if_fail (MM_IS_DEVICE (device));
-    g_return_if_fail (G_UDEV_IS_DEVICE (port));
+    g_return_if_fail (MM_IS_KERNEL_DEVICE (port));
 
     /* Create new cancellable task */
     task = g_task_new (self, cancellable, callback, user_data);
@@ -718,16 +716,16 @@ mm_plugin_supports_port (MMPlugin            *self,
                                  MM_CORE_ERROR_FAILED,
                                  "(%s) Missing port probe for port (%s/%s)",
                                  self->priv->name,
-                                 g_udev_device_get_subsystem (port),
-                                 g_udev_device_get_name (port));
+                                 mm_kernel_device_get_subsystem (port),
+                                 mm_kernel_device_get_name (port));
         g_object_unref (task);
         return;
     }
 
     /* Before launching any probing, check if the port is a net device. */
-    if (g_str_equal (g_udev_device_get_subsystem (port), "net")) {
+    if (g_str_equal (mm_kernel_device_get_subsystem (port), "net")) {
         mm_dbg ("(%s) [%s] probing deferred until result suggested",
-                self->priv->name, g_udev_device_get_name (port));
+                self->priv->name, mm_kernel_device_get_name (port));
         g_task_return_int (task, MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED);
         g_object_unref (task);
         return;
@@ -735,7 +733,7 @@ mm_plugin_supports_port (MMPlugin            *self,
 
     /* Build flags depending on what probing needed */
     probe_run_flags = MM_PORT_PROBE_NONE;
-    if (!g_str_has_prefix (g_udev_device_get_name (port), "cdc-wdm")) {
+    if (!g_str_has_prefix (mm_kernel_device_get_name (port), "cdc-wdm")) {
         /* Serial ports... */
         if (self->priv->at)
             probe_run_flags |= MM_PORT_PROBE_AT;
@@ -745,9 +743,9 @@ mm_plugin_supports_port (MMPlugin            *self,
             probe_run_flags |= MM_PORT_PROBE_QCDM;
     } else {
         /* cdc-wdm ports... */
-        if (self->priv->qmi && !g_strcmp0 (mm_device_utils_get_port_driver (port), "qmi_wwan"))
+        if (self->priv->qmi && !g_strcmp0 (mm_kernel_device_get_driver (port), "qmi_wwan"))
             probe_run_flags |= MM_PORT_PROBE_QMI;
-        else if (self->priv->mbim && !g_strcmp0 (mm_device_utils_get_port_driver (port), "cdc_mbim"))
+        else if (self->priv->mbim && !g_strcmp0 (mm_kernel_device_get_driver (port), "cdc_mbim"))
             probe_run_flags |= MM_PORT_PROBE_MBIM;
         else
             probe_run_flags |= MM_PORT_PROBE_AT;
@@ -780,7 +778,7 @@ mm_plugin_supports_port (MMPlugin            *self,
         mm_dbg ("(%s) [%s] not setting up AT probing tasks: "
                 "modem already has the expected single AT port",
                 self->priv->name,
-                g_udev_device_get_name (port));
+                mm_kernel_device_get_name (port));
 
         /* Assuming it won't be an AT port. We still run the probe anyway, in
          * case we need to check for other port types (e.g. QCDM) */
@@ -800,7 +798,7 @@ mm_plugin_supports_port (MMPlugin            *self,
     probe_list_str = mm_port_probe_flag_build_string_from_mask (ctx->flags);
     mm_dbg ("(%s) [%s] probe required: '%s'",
             self->priv->name,
-            g_udev_device_get_name (port),
+            mm_kernel_device_get_name (port),
             probe_list_str);
     g_free (probe_list_str);
 
@@ -819,9 +817,9 @@ mm_plugin_supports_port (MMPlugin            *self,
 /*****************************************************************************/
 
 MMPluginSupportsHint
-mm_plugin_discard_port_early (MMPlugin *self,
-                              MMDevice *device,
-                              GUdevDevice *port)
+mm_plugin_discard_port_early (MMPlugin       *self,
+                              MMDevice       *device,
+                              MMKernelDevice *port)
 {
     gboolean need_vendor_probing = FALSE;
     gboolean need_product_probing = FALSE;
@@ -913,7 +911,7 @@ mm_plugin_create_modem (MMPlugin  *self,
             }
 #if !defined WITH_QMI
             else if (mm_port_probe_get_port_type (probe) == MM_PORT_TYPE_NET &&
-                     !g_strcmp0 (mm_device_utils_get_port_driver (mm_port_probe_peek_port (probe)), "qmi_wwan")) {
+                     !g_strcmp0 (mm_kernel_device_get_driver (mm_port_probe_peek_port (probe)), "qmi_wwan")) {
                 /* Try to generically grab the port, but flagged as ignored */
                 grabbed = mm_base_modem_grab_port (modem,
                                                    mm_port_probe_get_port_subsys (probe),
@@ -926,7 +924,7 @@ mm_plugin_create_modem (MMPlugin  *self,
 #endif
 #if !defined WITH_MBIM
             else if (mm_port_probe_get_port_type (probe) == MM_PORT_TYPE_NET &&
-                     !g_strcmp0 (mm_device_utils_get_port_driver (mm_port_probe_peek_port (probe)), "cdc_mbim")) {
+                     !g_strcmp0 (mm_kernel_device_get_driver (mm_port_probe_peek_port (probe)), "cdc_mbim")) {
                 /* Try to generically grab the port, but flagged as ignored */
                 grabbed = mm_base_modem_grab_port (modem,
                                                    mm_port_probe_get_port_subsys (probe),
diff --git a/src/mm-plugin.h b/src/mm-plugin.h
index afe300a..001f278 100644
--- a/src/mm-plugin.h
+++ b/src/mm-plugin.h
@@ -20,12 +20,12 @@
 
 #include <glib.h>
 #include <glib-object.h>
-#include <gudev/gudev.h>
 
 #include "mm-base-modem.h"
 #include "mm-port.h"
 #include "mm-port-probe.h"
 #include "mm-device.h"
+#include "mm-kernel-device.h"
 
 #define MM_PLUGIN_GENERIC_NAME "Generic"
 #define MM_PLUGIN_MAJOR_VERSION 4
@@ -125,13 +125,13 @@ const gchar *mm_plugin_get_name (MMPlugin *plugin);
 
 /* This method will run all pre-probing filters, to see if we can discard this
  * plugin from the probing logic as soon as possible. */
-MMPluginSupportsHint mm_plugin_discard_port_early (MMPlugin *plugin,
-                                                   MMDevice *device,
-                                                   GUdevDevice *port);
+MMPluginSupportsHint mm_plugin_discard_port_early (MMPlugin       *plugin,
+                                                   MMDevice       *device,
+                                                   MMKernelDevice *port);
 
 void                   mm_plugin_supports_port        (MMPlugin             *plugin,
                                                        MMDevice             *device,
-                                                       GUdevDevice          *port,
+                                                       MMKernelDevice       *port,
                                                        GCancellable         *cancellable,
                                                        GAsyncReadyCallback   callback,
                                                        gpointer              user_data);
diff --git a/src/mm-port-probe.c b/src/mm-port-probe.c
index 908b134..0b4d227 100644
--- a/src/mm-port-probe.c
+++ b/src/mm-port-probe.c
@@ -74,8 +74,7 @@ static GParamSpec *properties[PROP_LAST];
 struct _MMPortProbePrivate {
     /* Properties */
     MMDevice *device;
-    GUdevDevice *port;
-    GUdevDevice *parent;
+    MMKernelDevice *port;
 
     /* Probing results */
     guint32 flags;
@@ -151,8 +150,8 @@ mm_port_probe_set_result_at (MMPortProbe *self,
 
     if (self->priv->is_at) {
         mm_dbg ("(%s/%s) port is AT-capable",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
 
         /* Also set as not a QCDM/QMI/MBIM port */
         self->priv->is_qcdm = FALSE;
@@ -161,8 +160,8 @@ mm_port_probe_set_result_at (MMPortProbe *self,
         self->priv->flags |= (MM_PORT_PROBE_QCDM | MM_PORT_PROBE_QMI | MM_PORT_PROBE_MBIM);
     } else {
         mm_dbg ("(%s/%s) port is not AT-capable",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
         self->priv->vendor = NULL;
         self->priv->product = NULL;
         self->priv->is_icera = FALSE;
@@ -178,14 +177,14 @@ mm_port_probe_set_result_at_vendor (MMPortProbe *self,
 {
     if (at_vendor) {
         mm_dbg ("(%s/%s) vendor probing finished",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
         self->priv->vendor = g_utf8_casefold (at_vendor, -1);
         self->priv->flags |= MM_PORT_PROBE_AT_VENDOR;
     } else {
         mm_dbg ("(%s/%s) couldn't probe for vendor string",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
         self->priv->vendor = NULL;
         self->priv->product = NULL;
         self->priv->flags |= (MM_PORT_PROBE_AT_VENDOR | MM_PORT_PROBE_AT_PRODUCT);
@@ -198,14 +197,14 @@ mm_port_probe_set_result_at_product (MMPortProbe *self,
 {
     if (at_product) {
         mm_dbg ("(%s/%s) product probing finished",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
         self->priv->product = g_utf8_casefold (at_product, -1);
         self->priv->flags |= MM_PORT_PROBE_AT_PRODUCT;
     } else {
         mm_dbg ("(%s/%s) couldn't probe for product string",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
         self->priv->product = NULL;
         self->priv->flags |= MM_PORT_PROBE_AT_PRODUCT;
     }
@@ -217,14 +216,14 @@ mm_port_probe_set_result_at_icera (MMPortProbe *self,
 {
     if (is_icera) {
         mm_dbg ("(%s/%s) Modem is Icera-based",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
         self->priv->is_icera = TRUE;
         self->priv->flags |= MM_PORT_PROBE_AT_ICERA;
     } else {
         mm_dbg ("(%s/%s) Modem is probably not Icera-based",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
         self->priv->is_icera = FALSE;
         self->priv->flags |= MM_PORT_PROBE_AT_ICERA;
     }
@@ -239,8 +238,8 @@ mm_port_probe_set_result_qcdm (MMPortProbe *self,
 
     if (self->priv->is_qcdm) {
         mm_dbg ("(%s/%s) port is QCDM-capable",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
 
         /* Also set as not an AT/QMI/MBIM port */
         self->priv->is_at = FALSE;
@@ -257,8 +256,8 @@ mm_port_probe_set_result_qcdm (MMPortProbe *self,
                               MM_PORT_PROBE_MBIM);
     } else
         mm_dbg ("(%s/%s) port is not QCDM-capable",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
 }
 
 void
@@ -270,8 +269,8 @@ mm_port_probe_set_result_qmi (MMPortProbe *self,
 
     if (self->priv->is_qmi) {
         mm_dbg ("(%s/%s) port is QMI-capable",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
 
         /* Also set as not an AT/QCDM/MBIM port */
         self->priv->is_at = FALSE;
@@ -287,8 +286,8 @@ mm_port_probe_set_result_qmi (MMPortProbe *self,
                               MM_PORT_PROBE_MBIM);
     } else
         mm_dbg ("(%s/%s) port is not QMI-capable",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
 }
 
 void
@@ -300,8 +299,8 @@ mm_port_probe_set_result_mbim (MMPortProbe *self,
 
     if (self->priv->is_mbim) {
         mm_dbg ("(%s/%s) port is MBIM-capable",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
 
         /* Also set as not an AT/QCDM/QMI port */
         self->priv->is_at = FALSE;
@@ -317,8 +316,8 @@ mm_port_probe_set_result_mbim (MMPortProbe *self,
                               MM_PORT_PROBE_QMI);
     } else
         mm_dbg ("(%s/%s) port is not MBIM-capable",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
 }
 
 /*****************************************************************************/
@@ -445,8 +444,8 @@ port_qmi_open_ready (MMPortQmi    *port_qmi,
     is_qmi = mm_port_qmi_open_finish (port_qmi, res, &error);
     if (!is_qmi) {
         mm_dbg ("(%s/%s) error checking QMI support: '%s'",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port),
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port),
                 error ? error->message : "unknown error");
         g_clear_error (&error);
     }
@@ -471,11 +470,11 @@ wdm_probe_qmi (MMPortProbe *self)
 
 #if defined WITH_QMI
     mm_dbg ("(%s/%s) probing QMI...",
-            g_udev_device_get_subsystem (self->priv->port),
-            g_udev_device_get_name (self->priv->port));
+            mm_kernel_device_get_subsystem (self->priv->port),
+            mm_kernel_device_get_name (self->priv->port));
 
     /* Create a port and try to open it */
-    ctx->port_qmi = mm_port_qmi_new (g_udev_device_get_name (self->priv->port));
+    ctx->port_qmi = mm_port_qmi_new (mm_kernel_device_get_name (self->priv->port));
     mm_port_qmi_open (ctx->port_qmi,
                       FALSE,
                       NULL,
@@ -521,8 +520,8 @@ mbim_port_open_ready (MMPortMbim   *mbim_port,
     is_mbim = mm_port_mbim_open_finish (mbim_port, res, &error);
     if (!is_mbim) {
         mm_dbg ("(%s/%s) error checking MBIM support: '%s'",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port),
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port),
                 error ? error->message : "unknown error");
         g_clear_error (&error);
     }
@@ -547,11 +546,11 @@ wdm_probe_mbim (MMPortProbe *self)
 
 #if defined WITH_MBIM
     mm_dbg ("(%s/%s) probing MBIM...",
-            g_udev_device_get_subsystem (self->priv->port),
-            g_udev_device_get_name (self->priv->port));
+            mm_kernel_device_get_subsystem (self->priv->port),
+            mm_kernel_device_get_name (self->priv->port));
 
     /* Create a port and try to open it */
-    ctx->mbim_port = mm_port_mbim_new (g_udev_device_get_name (self->priv->port));
+    ctx->mbim_port = mm_port_mbim_new (mm_kernel_device_get_name (self->priv->port));
     mm_port_mbim_open (ctx->mbim_port,
                        NULL,
                        (GAsyncReadyCallback) mbim_port_open_ready,
@@ -623,8 +622,8 @@ serial_probe_qcdm_parse_response (MMPortSerialQcdm *port,
         result = qcdm_cmd_version_info_result ((const gchar *) response->data, response->len, &err);
         if (!result) {
             mm_warn ("(%s/%s) failed to parse QCDM version info command result: %d",
-                     g_udev_device_get_subsystem (self->priv->port),
-                     g_udev_device_get_name (self->priv->port),
+                     mm_kernel_device_get_subsystem (self->priv->port),
+                     mm_kernel_device_get_name (self->priv->port),
                      err);
             retry = TRUE;
         } else {
@@ -687,8 +686,8 @@ serial_probe_qcdm (MMPortProbe *self)
         return G_SOURCE_REMOVE;
 
     mm_dbg ("(%s/%s) probing QCDM...",
-            g_udev_device_get_subsystem (self->priv->port),
-            g_udev_device_get_name (self->priv->port));
+            mm_kernel_device_get_subsystem (self->priv->port),
+            mm_kernel_device_get_name (self->priv->port));
 
     /* If open, close the AT port */
     if (ctx->serial) {
@@ -702,14 +701,14 @@ serial_probe_qcdm (MMPortProbe *self)
     }
 
     /* Open the QCDM port */
-    ctx->serial = MM_PORT_SERIAL (mm_port_serial_qcdm_new (g_udev_device_get_name (self->priv->port)));
+    ctx->serial = MM_PORT_SERIAL (mm_port_serial_qcdm_new (mm_kernel_device_get_name (self->priv->port)));
     if (!ctx->serial) {
         port_probe_task_return_error (self,
                                       g_error_new (MM_CORE_ERROR,
                                                    MM_CORE_ERROR_FAILED,
                                                    "(%s/%s) Couldn't create QCDM port",
-                                                   g_udev_device_get_subsystem (self->priv->port),
-                                                   g_udev_device_get_name (self->priv->port)));
+                                                   mm_kernel_device_get_subsystem (self->priv->port),
+                                                   mm_kernel_device_get_name (self->priv->port)));
         return G_SOURCE_REMOVE;
     }
 
@@ -719,8 +718,8 @@ serial_probe_qcdm (MMPortProbe *self)
                                       g_error_new (MM_SERIAL_ERROR,
                                                    MM_SERIAL_ERROR_OPEN_FAILED,
                                                    "(%s/%s) Failed to open QCDM port: %s",
-                                                   g_udev_device_get_subsystem (self->priv->port),
-                                                   g_udev_device_get_name (self->priv->port),
+                                                   mm_kernel_device_get_subsystem (self->priv->port),
+                                                   mm_kernel_device_get_name (self->priv->port),
                                                    (error ? error->message : "unknown error")));
         g_clear_error (&error);
         return G_SOURCE_REMOVE;
@@ -739,9 +738,9 @@ serial_probe_qcdm (MMPortProbe *self)
         port_probe_task_return_error (self,
                                       g_error_new (MM_SERIAL_ERROR,
                                                    MM_SERIAL_ERROR_OPEN_FAILED,
-                                                   "(%s/%s) Failed to create QCDM versin info command",
-                                                   g_udev_device_get_subsystem (self->priv->port),
-                                                   g_udev_device_get_name (self->priv->port)));
+                                                   "(%s/%s) Failed to create QCDM version info command",
+                                                   mm_kernel_device_get_subsystem (self->priv->port),
+                                                   mm_kernel_device_get_name (self->priv->port)));
         return G_SOURCE_REMOVE;
     }
     verinfo->len = len + 1;
@@ -899,8 +898,8 @@ serial_probe_at_parse_response (MMPortSerialAt *port,
     /* If AT probing cancelled, end this partial probing */
     if (g_cancellable_is_cancelled (ctx->at_probing_cancellable)) {
         mm_dbg ("(%s/%s) no need to keep on probing the port for AT support",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
         ctx->at_result_processor (self, NULL);
         serial_probe_schedule (self);
         return;
@@ -920,8 +919,8 @@ serial_probe_at_parse_response (MMPortSerialAt *port,
                                           g_error_new (MM_CORE_ERROR,
                                                        MM_CORE_ERROR_UNSUPPORTED,
                                                        "(%s/%s) error while probing AT features: %s",
-                                                       g_udev_device_get_subsystem (self->priv->port),
-                                                       g_udev_device_get_name (self->priv->port),
+                                                       mm_kernel_device_get_subsystem (self->priv->port),
+                                                       mm_kernel_device_get_name (self->priv->port),
                                                        result_error->message));
             goto out;
         }
@@ -942,8 +941,8 @@ serial_probe_at_parse_response (MMPortSerialAt *port,
             ctx->source_id = g_idle_add ((GSourceFunc) serial_probe_at, self);
         else {
             mm_dbg ("(%s/%s) re-scheduling next command in probing group in %u seconds...",
-                    g_udev_device_get_subsystem (self->priv->port),
-                    g_udev_device_get_name (self->priv->port),
+                    mm_kernel_device_get_subsystem (self->priv->port),
+                    mm_kernel_device_get_name (self->priv->port),
                     ctx->at_commands_wait_secs);
             ctx->source_id = g_timeout_add_seconds (ctx->at_commands_wait_secs, (GSourceFunc) serial_probe_at, self);
         }
@@ -979,8 +978,8 @@ serial_probe_at (MMPortProbe *self)
     /* If AT probing cancelled, end this partial probing */
     if (g_cancellable_is_cancelled (ctx->at_probing_cancellable)) {
         mm_dbg ("(%s/%s) no need to launch probing for AT support",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
         ctx->at_result_processor (self, NULL);
         serial_probe_schedule (self);
         return G_SOURCE_REMOVE;
@@ -1156,8 +1155,8 @@ serial_buffer_full (MMPortSerial *serial,
     ctx = g_task_get_task_data (self->priv->task);
 
     mm_dbg ("(%s/%s) serial buffer full",
-            g_udev_device_get_subsystem (self->priv->port),
-            g_udev_device_get_name (self->priv->port));
+            mm_kernel_device_get_subsystem (self->priv->port),
+            mm_kernel_device_get_name (self->priv->port));
     /* Don't explicitly close the AT port, just end the AT probing
      * (or custom init probing) */
     mm_port_probe_set_result_at (self, FALSE);
@@ -1200,17 +1199,17 @@ serial_open_at (MMPortProbe *self)
         gpointer parser;
         MMPortSubsys subsys = MM_PORT_SUBSYS_TTY;
 
-        if (g_str_has_prefix (g_udev_device_get_subsystem (self->priv->port), "usb"))
+        if (g_str_has_prefix (mm_kernel_device_get_subsystem (self->priv->port), "usb"))
             subsys = MM_PORT_SUBSYS_USB;
 
-        ctx->serial = MM_PORT_SERIAL (mm_port_serial_at_new (g_udev_device_get_name (self->priv->port), subsys));
+        ctx->serial = MM_PORT_SERIAL (mm_port_serial_at_new (mm_kernel_device_get_name (self->priv->port), subsys));
         if (!ctx->serial) {
             port_probe_task_return_error (self,
                                           g_error_new (MM_CORE_ERROR,
                                                        MM_CORE_ERROR_FAILED,
                                                        "(%s/%s) couldn't create AT port",
-                                                       g_udev_device_get_subsystem (self->priv->port),
-                                                       g_udev_device_get_name (self->priv->port)));
+                                                       mm_kernel_device_get_subsystem (self->priv->port),
+                                                       mm_kernel_device_get_name (self->priv->port)));
             return G_SOURCE_REMOVE;
         }
 
@@ -1240,8 +1239,8 @@ serial_open_at (MMPortProbe *self)
                                           g_error_new (MM_CORE_ERROR,
                                                        MM_CORE_ERROR_FAILED,
                                                        "(%s/%s) failed to open port after 4 tries",
-                                                       g_udev_device_get_subsystem (self->priv->port),
-                                                       g_udev_device_get_name (self->priv->port)));
+                                                       mm_kernel_device_get_subsystem (self->priv->port),
+                                                       mm_kernel_device_get_name (self->priv->port)));
             g_clear_error (&error);
             return G_SOURCE_REMOVE;
         }
@@ -1257,8 +1256,8 @@ serial_open_at (MMPortProbe *self)
                                       g_error_new (MM_SERIAL_ERROR,
                                                    MM_SERIAL_ERROR_OPEN_FAILED,
                                                    "(%s/%s) failed to open port: %s",
-                                                   g_udev_device_get_subsystem (self->priv->port),
-                                                   g_udev_device_get_name (self->priv->port),
+                                                   mm_kernel_device_get_subsystem (self->priv->port),
+                                                   mm_kernel_device_get_name (self->priv->port),
                                                    (error ? error->message : "unknown error")));
         g_clear_error (&error);
         return G_SOURCE_REMOVE;
@@ -1297,8 +1296,8 @@ mm_port_probe_run_cancel_at_probing (MMPortProbe *self)
         return FALSE;
 
     mm_dbg ("(%s/%s) requested to cancel all AT probing",
-            g_udev_device_get_subsystem (self->priv->port),
-            g_udev_device_get_name (self->priv->port));
+            mm_kernel_device_get_subsystem (self->priv->port),
+            mm_kernel_device_get_name (self->priv->port));
     g_cancellable_cancel (ctx->at_probing_cancellable);
     return TRUE;
 }
@@ -1363,8 +1362,8 @@ mm_port_probe_run (MMPortProbe                *self,
     /* All requested probings already available? If so, we're done */
     if (!ctx->flags) {
         mm_dbg ("(%s/%s) port probing finished: no more probings needed",
-                g_udev_device_get_subsystem (self->priv->port),
-                g_udev_device_get_name (self->priv->port));
+                mm_kernel_device_get_subsystem (self->priv->port),
+                mm_kernel_device_get_name (self->priv->port));
         port_probe_task_return_boolean (self, TRUE);
         return;
     }
@@ -1372,8 +1371,8 @@ mm_port_probe_run (MMPortProbe                *self,
     /* Log the probes scheduled to be run */
     probe_list_str = mm_port_probe_flag_build_string_from_mask (ctx->flags);
     mm_dbg ("(%s/%s) launching port probing: '%s'",
-            g_udev_device_get_subsystem (self->priv->port),
-            g_udev_device_get_name (self->priv->port),
+            mm_kernel_device_get_subsystem (self->priv->port),
+            mm_kernel_device_get_name (self->priv->port),
             probe_list_str);
     g_free (probe_list_str);
 
@@ -1417,8 +1416,8 @@ mm_port_probe_is_at (MMPortProbe *self)
 
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
 
-    subsys = g_udev_device_get_subsystem (self->priv->port);
-    name = g_udev_device_get_name (self->priv->port);
+    subsys = mm_kernel_device_get_subsystem (self->priv->port);
+    name = mm_kernel_device_get_name (self->priv->port);
     if (g_str_equal (subsys, "net"))
         return FALSE;
 
@@ -1452,8 +1451,8 @@ mm_port_probe_is_qcdm (MMPortProbe *self)
 
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
 
-    subsys = g_udev_device_get_subsystem (self->priv->port);
-    name = g_udev_device_get_name (self->priv->port);
+    subsys = mm_kernel_device_get_subsystem (self->priv->port);
+    name = mm_kernel_device_get_name (self->priv->port);
     if (g_str_equal (subsys, "net") ||
         (g_str_has_prefix (subsys, "usb") &&
          g_str_has_prefix (name, "cdc-wdm")))
@@ -1472,8 +1471,8 @@ mm_port_probe_is_qmi (MMPortProbe *self)
 
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
 
-    subsys = g_udev_device_get_subsystem (self->priv->port);
-    name = g_udev_device_get_name (self->priv->port);
+    subsys = mm_kernel_device_get_subsystem (self->priv->port);
+    name = mm_kernel_device_get_name (self->priv->port);
     if (!g_str_has_prefix (subsys, "usb") ||
         !name ||
         !g_str_has_prefix (name, "cdc-wdm"))
@@ -1506,8 +1505,8 @@ mm_port_probe_is_mbim (MMPortProbe *self)
 
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
 
-    subsys = g_udev_device_get_subsystem (self->priv->port);
-    name = g_udev_device_get_name (self->priv->port);
+    subsys = mm_kernel_device_get_subsystem (self->priv->port);
+    name = mm_kernel_device_get_name (self->priv->port);
     if (!g_str_has_prefix (subsys, "usb") ||
         !name ||
         !g_str_has_prefix (name, "cdc-wdm"))
@@ -1540,8 +1539,8 @@ mm_port_probe_get_port_type (MMPortProbe *self)
 
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
 
-    subsys = g_udev_device_get_subsystem (self->priv->port);
-    name = g_udev_device_get_name (self->priv->port);
+    subsys = mm_kernel_device_get_subsystem (self->priv->port);
+    name = mm_kernel_device_get_name (self->priv->port);
 
     if (g_str_equal (subsys, "net"))
         return MM_PORT_TYPE_NET;
@@ -1587,7 +1586,7 @@ mm_port_probe_get_device (MMPortProbe *self)
     return MM_DEVICE (g_object_ref (self->priv->device));
 }
 
-GUdevDevice *
+MMKernelDevice *
 mm_port_probe_peek_port (MMPortProbe *self)
 {
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL);
@@ -1595,12 +1594,12 @@ mm_port_probe_peek_port (MMPortProbe *self)
     return self->priv->port;
 };
 
-GUdevDevice *
+MMKernelDevice *
 mm_port_probe_get_port (MMPortProbe *self)
 {
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL);
 
-    return G_UDEV_DEVICE (g_object_ref (self->priv->port));
+    return MM_KERNEL_DEVICE (g_object_ref (self->priv->port));
 };
 
 const gchar *
@@ -1611,8 +1610,8 @@ mm_port_probe_get_vendor (MMPortProbe *self)
 
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
 
-    subsys = g_udev_device_get_subsystem (self->priv->port);
-    name = g_udev_device_get_name (self->priv->port);
+    subsys = mm_kernel_device_get_subsystem (self->priv->port);
+    name = mm_kernel_device_get_name (self->priv->port);
     if (g_str_equal (subsys, "net") ||
         (g_str_has_prefix (subsys, "usb") &&
          g_str_has_prefix (name, "cdc-wdm")))
@@ -1631,8 +1630,8 @@ mm_port_probe_get_product (MMPortProbe *self)
 
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
 
-    subsys = g_udev_device_get_subsystem (self->priv->port);
-    name = g_udev_device_get_name (self->priv->port);
+    subsys = mm_kernel_device_get_subsystem (self->priv->port);
+    name = mm_kernel_device_get_name (self->priv->port);
     if (g_str_equal (subsys, "net") ||
         (g_str_has_prefix (subsys, "usb") &&
          g_str_has_prefix (name, "cdc-wdm")))
@@ -1648,7 +1647,7 @@ mm_port_probe_is_icera (MMPortProbe *self)
 {
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
 
-    if (g_str_equal (g_udev_device_get_subsystem (self->priv->port), "net"))
+    if (g_str_equal (mm_kernel_device_get_subsystem (self->priv->port), "net"))
         return FALSE;
 
     return (self->priv->flags & MM_PORT_PROBE_AT_ICERA ?
@@ -1682,7 +1681,7 @@ mm_port_probe_get_port_name (MMPortProbe *self)
 {
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL);
 
-    return g_udev_device_get_name (self->priv->port);
+    return mm_kernel_device_get_name (self->priv->port);
 }
 
 const gchar *
@@ -1690,7 +1689,7 @@ mm_port_probe_get_port_subsys (MMPortProbe *self)
 {
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL);
 
-    return g_udev_device_get_subsystem (self->priv->port);
+    return mm_kernel_device_get_subsystem (self->priv->port);
 }
 
 const gchar *
@@ -1698,14 +1697,14 @@ mm_port_probe_get_parent_path (MMPortProbe *self)
 {
     g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL);
 
-    return (self->priv->parent ? g_udev_device_get_sysfs_path (self->priv->parent) : NULL);
+    return mm_kernel_device_get_parent_sysfs_path (self->priv->port);
 }
 
 /*****************************************************************************/
 
 MMPortProbe *
-mm_port_probe_new (MMDevice *device,
-                   GUdevDevice *port)
+mm_port_probe_new (MMDevice       *device,
+                   MMKernelDevice *port)
 {
     return MM_PORT_PROBE (g_object_new (MM_TYPE_PORT_PROBE,
                                         MM_PORT_PROBE_DEVICE, device,
@@ -1737,8 +1736,7 @@ set_property (GObject *object,
     case PROP_PORT:
         /* construct only */
         self->priv->port = g_value_dup_object (value);
-        self->priv->parent = g_udev_device_get_parent (self->priv->port);
-        self->priv->is_ignored = g_udev_device_get_property_as_boolean (self->priv->port, "ID_MM_PORT_IGNORE");
+        self->priv->is_ignored = mm_kernel_device_get_property_as_boolean (self->priv->port, "ID_MM_PORT_IGNORE");
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1789,7 +1787,6 @@ dispose (GObject *object)
     /* We didn't get a reference to the device */
     self->priv->device = NULL;
 
-    g_clear_object (&self->priv->parent);
     g_clear_object (&self->priv->port);
 
     G_OBJECT_CLASS (mm_port_probe_parent_class)->dispose (object);
@@ -1819,8 +1816,8 @@ mm_port_probe_class_init (MMPortProbeClass *klass)
     properties[PROP_PORT] =
         g_param_spec_object (MM_PORT_PROBE_PORT,
                              "Port",
-                             "UDev device object of the port",
-                             G_UDEV_TYPE_DEVICE,
+                             "kernel device object of the port",
+                             MM_TYPE_KERNEL_DEVICE,
                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
     g_object_class_install_property (object_class, PROP_PORT, properties[PROP_PORT]);
 }
diff --git a/src/mm-port-probe.h b/src/mm-port-probe.h
index 332cb5a..ba8d100 100644
--- a/src/mm-port-probe.h
+++ b/src/mm-port-probe.h
@@ -21,11 +21,11 @@
 #include <glib.h>
 #include <glib-object.h>
 #include <gio/gio.h>
-#include <gudev/gudev.h>
 
 #include "mm-private-boxed-types.h"
 #include "mm-port-probe-at.h"
 #include "mm-port-serial-at.h"
+#include "mm-kernel-device.h"
 #include "mm-device.h"
 
 #define MM_TYPE_PORT_PROBE            (mm_port_probe_get_type ())
@@ -78,16 +78,16 @@ typedef gboolean (* MMPortProbeAtCustomInitFinish) (MMPortProbe *probe,
 
 GType mm_port_probe_get_type (void);
 
-MMPortProbe *mm_port_probe_new (MMDevice *device,
-                                GUdevDevice *port);
+MMPortProbe *mm_port_probe_new (MMDevice       *device,
+                                MMKernelDevice *port);
 
-MMDevice    *mm_port_probe_peek_device      (MMPortProbe *self);
-MMDevice    *mm_port_probe_get_device       (MMPortProbe *self);
-GUdevDevice *mm_port_probe_peek_port        (MMPortProbe *self);
-GUdevDevice *mm_port_probe_get_port         (MMPortProbe *self);
-const gchar *mm_port_probe_get_port_name    (MMPortProbe *self);
-const gchar *mm_port_probe_get_port_subsys  (MMPortProbe *self);
-const gchar *mm_port_probe_get_parent_path  (MMPortProbe *self);
+MMDevice       *mm_port_probe_peek_device     (MMPortProbe *self);
+MMDevice       *mm_port_probe_get_device      (MMPortProbe *self);
+MMKernelDevice *mm_port_probe_peek_port       (MMPortProbe *self);
+MMKernelDevice *mm_port_probe_get_port        (MMPortProbe *self);
+const gchar    *mm_port_probe_get_port_name   (MMPortProbe *self);
+const gchar    *mm_port_probe_get_port_subsys (MMPortProbe *self);
+const gchar    *mm_port_probe_get_parent_path (MMPortProbe *self);
 
 /* Probing result setters */
 void mm_port_probe_set_result_at         (MMPortProbe *self,
-- 
2.9.0



More information about the ModemManager-devel mailing list