diff --git a/bin/input-monitor.c b/bin/input-monitor.c
index 8d3e407f77b70981134fe2a8adccef93e0ea11dd..7d9efd21c39d90d295dbc0e251cf31a95b91b72a 100644
--- a/bin/input-monitor.c
+++ b/bin/input-monitor.c
@@ -43,6 +43,7 @@
 static FILE *original_stdout = NULL;
 static gboolean opt_one_line = FALSE;
 static gboolean opt_seq = FALSE;
+static gboolean opt_verbose = FALSE;
 
 static void
 print_json (JsonBuilder *builder)
@@ -114,6 +115,46 @@ added (SrtInputDeviceMonitor *monitor,
               json_builder_end_array (builder);
             }
 
+          if (opt_verbose)
+            {
+              g_autofree gchar *uevent = srt_input_device_dup_uevent (dev);
+
+              if (uevent != NULL)
+                {
+                  const char *start = uevent;
+                  const char *end;
+
+                  /* uevent is conceptually a single string, but we
+                   * present it as an array of lines here, because that's
+                   * a lot easier to read! */
+                  json_builder_set_member_name (builder, "uevent");
+                  json_builder_begin_array (builder);
+
+                  while (*start != '\0')
+                    {
+                      g_autofree gchar *valid = NULL;
+
+                      while (*start == '\n')
+                        start++;
+
+                      if (*start == '\0')
+                        break;
+
+                      end = strchrnul (start, '\n');
+
+                      valid = g_utf8_make_valid (start, (end - start) - 1);
+                      json_builder_add_string_value (builder, valid);
+
+                      if (*end == '\0')
+                        break;
+
+                      start = end + 1;
+                    }
+
+                  json_builder_end_array (builder);
+                }
+            }
+
           /* TODO: Print more details here */
         }
       json_builder_end_object (builder);
@@ -236,6 +277,8 @@ static const GOptionEntry option_entries[] =
     NULL },
   { "udev", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, opt_udev_cb,
     "Find devices using udev", NULL },
+  { "verbose", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_verbose,
+    "Be more verbose", NULL },
   { "version", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_print_version,
     "Print version number and exit", NULL },
   { NULL }
diff --git a/steam-runtime-tools/input-device-internal.h b/steam-runtime-tools/input-device-internal.h
index 3d9b64d57aa64275587d0036f81514bfebb6d2ef..02acf67cc57b2aac3e9c74be720b5e0158fdfb52 100644
--- a/steam-runtime-tools/input-device-internal.h
+++ b/steam-runtime-tools/input-device-internal.h
@@ -36,6 +36,7 @@ struct _SrtInputDeviceInterface
   const char * (*get_sys_path) (SrtInputDevice *device);
   const char * (*get_subsystem) (SrtInputDevice *device);
   gchar ** (*dup_udev_properties) (SrtInputDevice *device);
+  gchar * (*dup_uevent) (SrtInputDevice *device);
 };
 
 struct _SrtInputDeviceMonitorInterface
diff --git a/steam-runtime-tools/input-device.c b/steam-runtime-tools/input-device.c
index cdf89fa09e1f8b6018845fa9dd390c5c6606b05d..7188bbf30cfdc143baeae719ef44c3381111bede 100644
--- a/steam-runtime-tools/input-device.c
+++ b/steam-runtime-tools/input-device.c
@@ -135,6 +135,49 @@ srt_input_device_get_subsystem (SrtInputDevice *device)
   return iface->get_subsystem (device);
 }
 
+/**
+ * srt_input_device_dup_uevent:
+ * @device: An object implementing #SrtInputDeviceInterface
+ *
+ * Return the `uevent` data from the kernel.
+ *
+ * Returns: (nullable) (transfer full): A multi-line string, or %NULL.
+ *  Free with g_free().
+ */
+gchar *
+srt_input_device_dup_uevent (SrtInputDevice *device)
+{
+  SrtInputDeviceInterface *iface = SRT_INPUT_DEVICE_GET_INTERFACE (device);
+
+  g_return_val_if_fail (iface != NULL, NULL);
+
+  return iface->dup_uevent (device);
+}
+
+/*
+ * Default implementation: read the file in /sys. The kernel provides this,
+ * so it should always be present, except in containers.
+ */
+static gchar *
+srt_input_device_default_dup_uevent (SrtInputDevice *device)
+{
+  g_autofree gchar *uevent_path = NULL;
+  g_autofree gchar *contents = NULL;
+  const char *sys_path;
+
+  sys_path = srt_input_device_get_sys_path (device);
+
+  if (sys_path == NULL)
+    return NULL;
+
+  uevent_path = g_build_filename (sys_path, "uevent", NULL);
+
+  if (g_file_get_contents (uevent_path, &contents, NULL, NULL))
+    return g_steal_pointer (&contents);
+
+  return NULL;
+}
+
 /**
  * srt_input_device_dup_udev_properties:
  * @device: An object implementing #SrtInputDeviceInterface
@@ -166,6 +209,7 @@ srt_input_device_default_init (SrtInputDeviceInterface *iface)
   IMPLEMENT2 (get_sys_path, get_null_string);
   IMPLEMENT2 (get_subsystem, get_null_string);
   IMPLEMENT2 (dup_udev_properties, get_null_strv);
+  IMPLEMENT (dup_uevent);
 
 #undef IMPLEMENT
 #undef IMPLEMENT2
diff --git a/steam-runtime-tools/input-device.h b/steam-runtime-tools/input-device.h
index 1db5782dd3bb0990581f42aed11d33d73821e5ce..67685b7366cbc67a6451a897fe7714a623599673 100644
--- a/steam-runtime-tools/input-device.h
+++ b/steam-runtime-tools/input-device.h
@@ -52,6 +52,8 @@ _SRT_PUBLIC
 const char *srt_input_device_get_subsystem (SrtInputDevice *device);
 _SRT_PUBLIC
 gchar **srt_input_device_dup_udev_properties (SrtInputDevice *device);
+_SRT_PUBLIC
+gchar *srt_input_device_dup_uevent (SrtInputDevice *device);
 
 /**
  * SrtInputDeviceMonitorFlags:
diff --git a/steam-runtime-tools/udev-input-device.c b/steam-runtime-tools/udev-input-device.c
index 2303ddaf4d77f99a5df13dea042b87e760ca8400..7245a7535079218fa177f2642ffe747a4b3cd9af 100644
--- a/steam-runtime-tools/udev-input-device.c
+++ b/steam-runtime-tools/udev-input-device.c
@@ -64,6 +64,7 @@ static struct
   const char *(*udev_device_get_syspath) (struct udev_device *);
   struct udev_list_entry *(*udev_device_get_properties_list_entry) (struct udev_device *);
   const char *(*udev_device_get_property_value) (struct udev_device *, const char *);
+  const char *(*udev_device_get_sysattr_value) (struct udev_device *, const char *);
   struct udev_device *(*udev_device_ref) (struct udev_device *);
   struct udev_device *(*udev_device_unref) (struct udev_device *);
 
@@ -208,6 +209,14 @@ srt_udev_input_device_dup_udev_properties (SrtInputDevice *device)
   return (gchar **) g_ptr_array_free (g_steal_pointer (&arr), FALSE);
 }
 
+static gchar *
+srt_udev_input_device_dup_uevent (SrtInputDevice *device)
+{
+  SrtUdevInputDevice *self = SRT_UDEV_INPUT_DEVICE (device);
+
+  return g_strdup (symbols.udev_device_get_sysattr_value (self->dev, "uevent"));
+}
+
 static void
 srt_udev_input_device_iface_init (SrtInputDeviceInterface *iface)
 {
@@ -217,6 +226,7 @@ srt_udev_input_device_iface_init (SrtInputDeviceInterface *iface)
   IMPLEMENT (get_sys_path);
   IMPLEMENT (get_subsystem);
   IMPLEMENT (dup_udev_properties);
+  IMPLEMENT (dup_uevent);
 
 #undef IMPLEMENT
 }
@@ -380,6 +390,7 @@ srt_udev_input_device_monitor_initable_init (GInitable *initable,
   SYMBOL (udev_device_get_syspath);
   SYMBOL (udev_device_get_properties_list_entry);
   SYMBOL (udev_device_get_property_value);
+  SYMBOL (udev_device_get_sysattr_value);
   SYMBOL (udev_device_ref);
   SYMBOL (udev_device_unref);
 
diff --git a/tests/input-device.c b/tests/input-device.c
index 7e82f344537f3251e04efb2910bb2869c4389cb6..74335137a54d4e20527e1e6273f993c8161eb983 100644
--- a/tests/input-device.c
+++ b/tests/input-device.c
@@ -103,6 +103,7 @@ test_input_device_usb (Fixture *f,
   g_autoptr(MockInputDevice) mock_device = mock_input_device_new ();
   SrtInputDevice *device = SRT_INPUT_DEVICE (mock_device);
   g_auto(GStrv) udev_properties = NULL;
+  g_autofree gchar *uevent = NULL;
 
   mock_device->dev_node = g_strdup ("/dev/input/event0");
   mock_device->sys_path = g_strdup ("/sys/devices/mock/usb/hid/input/input0/event0");
@@ -110,6 +111,7 @@ test_input_device_usb (Fixture *f,
   mock_device->udev_properties = g_new0 (gchar *, 2);
   mock_device->udev_properties[0] = g_strdup ("ID_INPUT_JOYSTICK=1");
   mock_device->udev_properties[1] = NULL;
+  mock_device->uevent = g_strdup ("A=a\nB=b\n");
 
   /* TODO: Fill in somewhat realistic details for a USB-attached
    * Steam Controller */
@@ -120,6 +122,9 @@ test_input_device_usb (Fixture *f,
                    "/sys/devices/mock/usb/hid/input/input0/event0");
   g_assert_cmpstr (srt_input_device_get_subsystem (device), ==, "input");
 
+  uevent = srt_input_device_dup_uevent (device);
+  g_assert_cmpstr (uevent, ==, "A=a\nB=b\n");
+
   udev_properties = srt_input_device_dup_udev_properties (device);
   g_assert_nonnull (udev_properties);
   g_assert_cmpstr (udev_properties[0], ==, "ID_INPUT_JOYSTICK=1");
@@ -158,8 +163,12 @@ device_added_cb (SrtInputDeviceMonitor *monitor,
    * so we have to just emit debug messages. */
   if (f->config->type == MOCK)
     {
+      g_autofree gchar *uevent = NULL;
       g_auto(GStrv) udev_properties = NULL;
 
+      uevent = srt_input_device_dup_uevent (device);
+      g_assert_cmpstr (uevent, ==, "ONE=1\nTWO=2\n");
+
       udev_properties = srt_input_device_dup_udev_properties (device);
       g_assert_nonnull (udev_properties);
       g_assert_cmpstr (udev_properties[0], ==, "ID_INPUT_JOYSTICK=1");
diff --git a/tests/mock-input-device.c b/tests/mock-input-device.c
index ecd0fae376347ffa05016298ed66b9fd31a62ee8..cbbc05824e3f0fd8bfb639512d010c450f973ae3 100644
--- a/tests/mock-input-device.c
+++ b/tests/mock-input-device.c
@@ -64,6 +64,7 @@ mock_input_device_finalize (GObject *object)
   g_clear_pointer (&self->dev_node, g_free);
   g_clear_pointer (&self->subsystem, g_free);
   g_clear_pointer (&self->udev_properties, g_strfreev);
+  g_clear_pointer (&self->uevent, g_free);
 
   G_OBJECT_CLASS (mock_input_device_parent_class)->finalize (object);
 }
@@ -108,6 +109,14 @@ mock_input_device_dup_udev_properties (SrtInputDevice *device)
   return g_strdupv (self->udev_properties);
 }
 
+static gchar *
+mock_input_device_dup_uevent (SrtInputDevice *device)
+{
+  MockInputDevice *self = MOCK_INPUT_DEVICE (device);
+
+  return g_strdup (self->uevent);
+}
+
 static void
 mock_input_device_iface_init (SrtInputDeviceInterface *iface)
 {
@@ -117,6 +126,7 @@ mock_input_device_iface_init (SrtInputDeviceInterface *iface)
   IMPLEMENT (get_sys_path);
   IMPLEMENT (get_subsystem);
   IMPLEMENT (dup_udev_properties);
+  IMPLEMENT (dup_uevent);
 
 #undef IMPLEMENT
 }
@@ -271,6 +281,7 @@ add_steam_controller (MockInputDeviceMonitor *self,
   device->sys_path = g_strdup_printf ("/sys/devices/mock/usb/hid/input/input0/event%s",
                                       tail);
   device->subsystem = g_strdup ("input");
+  device->uevent = g_strdup ("ONE=1\nTWO=2\n");
 
   g_ptr_array_add (arr, g_strdup ("ID_INPUT_JOYSTICK=1"));
   g_ptr_array_add (arr, NULL);
diff --git a/tests/mock-input-device.h b/tests/mock-input-device.h
index 8e294dc97c7b3073753048a98e7666c334d73b27..1f73593d79ab1ee1970f233b15344112ec532265 100644
--- a/tests/mock-input-device.h
+++ b/tests/mock-input-device.h
@@ -53,6 +53,7 @@ struct _MockInputDevice
   gchar *dev_node;
   gchar *subsystem;
   gchar **udev_properties;
+  gchar *uevent;
 };
 
 struct _MockInputDeviceClass