Add support for Port objects
authorDavid Zeuthen <davidz@redhat.com>
Sat, 28 Nov 2009 18:47:52 +0000 (13:47 -0500)
committerDavid Zeuthen <davidz@redhat.com>
Sat, 28 Nov 2009 18:47:52 +0000 (13:47 -0500)
This commit only includes support for ATA ports. Support for SAS and
FibreChannel will follow.

23 files changed:
data/Makefile.am
data/org.freedesktop.DeviceKit.Disks.Controller.xml
data/org.freedesktop.DeviceKit.Disks.Device.xml
data/org.freedesktop.DeviceKit.Disks.Port.xml [new file with mode: 0644]
data/org.freedesktop.DeviceKit.Disks.xml
doc/Makefile.am
doc/dbus/Makefile.am
doc/devkit-disks-docs.xml
src/Makefile.am
src/devkit-disks-controller-private.c
src/devkit-disks-controller-private.h
src/devkit-disks-controller.c
src/devkit-disks-daemon.c
src/devkit-disks-daemon.h
src/devkit-disks-device-private.c
src/devkit-disks-device-private.h
src/devkit-disks-device.c
src/devkit-disks-port-private.c [new file with mode: 0644]
src/devkit-disks-port-private.h [new file with mode: 0644]
src/devkit-disks-port.c [new file with mode: 0644]
src/devkit-disks-port.h [new file with mode: 0644]
src/devkit-disks-types.h
tools/devkit-disks.c

index 3b5f12e..bea1c84 100644 (file)
@@ -2,7 +2,12 @@
 NULL =
 
 dbusifdir = $(datadir)/dbus-1/interfaces
-dbusif_DATA = org.freedesktop.DeviceKit.Disks.xml org.freedesktop.DeviceKit.Disks.Device.xml org.freedesktop.DeviceKit.Disks.Controller.xml
+dbusif_DATA = \
+       org.freedesktop.DeviceKit.Disks.xml             \
+       org.freedesktop.DeviceKit.Disks.Device.xml      \
+       org.freedesktop.DeviceKit.Disks.Controller.xml  \
+       org.freedesktop.DeviceKit.Disks.Port.xml        \
+       $(NULL)
 
 servicedir       = $(datadir)/dbus-1/system-services
 service_in_files = org.freedesktop.DeviceKit.Disks.service.in
@@ -25,10 +30,8 @@ pkgconfigdir = $(datadir)/pkgconfig
 pkgconfig_DATA = DeviceKit-disks.pc
 
 EXTRA_DIST =                                           \
-       org.freedesktop.DeviceKit.Disks.xml             \
-       org.freedesktop.DeviceKit.Disks.Device.xml      \
-       org.freedesktop.DeviceKit.Disks.Controller.xml  \
        95-devkit-disks.rules                           \
+       $(dbusif_DATA)                                  \
        $(service_in_files)                             \
        $(dbusconf_in_files)                            \
        DeviceKit-disks.pc.in                           \
index 1833874..8f7128c 100644 (file)
       </doc:para></doc:description></doc:doc>
     </property>
 
+    <property name="NumPorts" type="u" access="read">
+      <doc:doc><doc:description><doc:para>
+            Number of physical ports on the storage controller or 0 if unknown.
+      </doc:para></doc:description></doc:doc>
+    </property>
+
+    <property name="Fabric" type="s" access="read">
+      <doc:doc><doc:description><doc:para>
+            The fabric used for the storage controller to communicate with other storage
+            targets and initiators or blank if unknown. Known values include
+            <doc:list>
+              <doc:item>
+                <doc:term>pata</doc:term><doc:definition>Parallel ATA</doc:definition>
+              </doc:item>
+              <doc:item>
+                <doc:term>sata</doc:term><doc:definition>Serial ATA</doc:definition>
+              </doc:item>
+              <doc:item>
+                <doc:term>esata</doc:term><doc:definition>External Serial ATA</doc:definition>
+              </doc:item>
+              <doc:item>
+                <doc:term>sas</doc:term><doc:definition>Serial Attached SCSI</doc:definition>
+              </doc:item>
+            </doc:list>
+            TODO: include other fabrics (FC, Parallel SCSI, etc.).
+      </doc:para></doc:description></doc:doc>
+    </property>
+
   </interface>
 
 </node>
index 900b0b4..b5684cb 100644 (file)
             is TRUE.
       </doc:para></doc:description></doc:doc>
     </property>
+    <property name="DrivePort" type="o" access="read">
+      <doc:doc><doc:description><doc:para>
+            The object of the port for the drive or "/" if no port exists.
+            This property is only valid if
+            <doc:ref type="property" to="Device:DeviceIsDrive">DeviceIsDrive</doc:ref>
+            is TRUE.
+      </doc:para></doc:description></doc:doc>
+    </property>
 
     <!-- **************************************************************************************************** -->
 
diff --git a/data/org.freedesktop.DeviceKit.Disks.Port.xml b/data/org.freedesktop.DeviceKit.Disks.Port.xml
new file mode 100644 (file)
index 0000000..be3cb83
--- /dev/null
@@ -0,0 +1,67 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" [
+  <!ENTITY ERROR_FAILED "org.freedesktop.DeviceKit.Disks.Error.Failed">
+  <!ENTITY ERROR_BUSY "org.freedesktop.DeviceKit.Disks.Error.Busy">
+  <!ENTITY ERROR_CANCELLED "org.freedesktop.DeviceKit.Disks.Error.Cancelled">
+  <!ENTITY ERROR_INHIBITED "org.freedesktop.DeviceKit.Disks.Error.Inhibited">
+  <!ENTITY ERROR_INVALID_OPTION "org.freedesktop.DeviceKit.Disks.Error.InvalidOption">
+  <!ENTITY ERROR_NOT_SUPPORTED "org.freedesktop.DeviceKit.Disks.Error.NotSupported">
+  <!ENTITY ERROR_ATA_SMART_WOULD_WAKEUP "org.freedesktop.DeviceKit.Disks.Error.NotFound">
+  <!ENTITY ERROR_NOT_AUTHORIZED "org.freedesktop.PolicyKit.Error.NotAuthorized">
+  <!ENTITY ERROR_FILESYSTEM_DRIVER_MISSING "org.freedesktop.DeviceKit.Disks.Error.FilesystemDriverMissing">
+  <!ENTITY ERROR_FILESYSTEM_TOOLS_MISSING "org.freedesktop.DeviceKit.Disks.Error.FilesystemToolsMissing">
+]>
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+  <interface name="org.freedesktop.DeviceKit.Disks.Port">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          This interface provides information about ports attached to
+          either an adapter or expansion device (e.g. SAS expanders or
+          SATA Port Multipliers).
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+
+    <!-- ************************************************************ -->
+
+    <signal name="Changed">
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Something on the port changed.
+          </doc:para>
+        </doc:description>
+      </doc:doc>
+    </signal>
+
+    <!-- ************************************************************ -->
+
+    <property name="NativePath" type="s" access="read">
+      <doc:doc><doc:description><doc:para>
+            OS specific native path of the port. On Linux this is the sysfs path, for example <doc:tt>/sys/devices/pci0000:00/0000:00:1f.1/host3/scsi_host/host3</doc:tt> or TODO:SAS example.
+      </doc:para></doc:description></doc:doc>
+    </property>
+
+    <property name="Controller" type="o" access="read">
+      <doc:doc><doc:description><doc:para>
+            The object path of the controller the port is attached to.
+      </doc:para></doc:description></doc:doc>
+    </property>
+
+    <property name="Parent" type="o" access="read">
+      <doc:doc><doc:description><doc:para>
+            The object path of the adapter or expansion device the port is attached to.
+      </doc:para></doc:description></doc:doc>
+    </property>
+
+    <property name="Number" type="i" access="read">
+      <doc:doc><doc:description><doc:para>
+            The number of the port (starting from zero) or -1 if unknown.
+      </doc:para></doc:description></doc:doc>
+    </property>
+
+  </interface>
+
+</node>
index 66dddcd..f419427 100644 (file)
 
     <!-- ************************************************************ -->
 
+    <method name="EnumeratePorts">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="devices" direction="out" type="ao">
+        <doc:doc><doc:summary>An array of object paths for ports.</doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Enumerate all storage ports on the system.
+          </doc:para>
+        </doc:description>
+      </doc:doc>
+    </method>
+
+    <!-- ************************************************************ -->
+
     <method name="EnumerateDevices">
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       <arg name="devices" direction="out" type="ao">
         </doc:description>
       </doc:doc>
     </signal>
-
-    <!-- ************************************************************ -->
-
     <signal name="ControllerRemoved">
       <arg name="controller" type="o">
         <doc:doc><doc:summary>Object path of controller that was removed.</doc:summary></doc:doc>
         </doc:description>
       </doc:doc>
     </signal>
-
-    <!-- ************************************************************ -->
-
     <signal name="ControllerChanged">
       <arg name="controller" type="o">
         <doc:doc><doc:summary>Object path of controller that was changed.</doc:summary></doc:doc>
 
     <!-- ************************************************************ -->
 
+    <signal name="PortAdded">
+      <arg name="port" type="o">
+        <doc:doc><doc:summary>Object path of port that was added.</doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Emitted when a port is added.
+          </doc:para>
+        </doc:description>
+      </doc:doc>
+    </signal>
+    <signal name="PortRemoved">
+      <arg name="port" type="o">
+        <doc:doc><doc:summary>Object path of port that was removed.</doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Emitted when a port is removed.
+          </doc:para>
+        </doc:description>
+      </doc:doc>
+    </signal>
+    <signal name="PortChanged">
+      <arg name="port" type="o">
+        <doc:doc><doc:summary>Object path of port that was changed.</doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Emitted when a port changed.
+          </doc:para>
+        </doc:description>
+      </doc:doc>
+    </signal>
+
+    <!-- ************************************************************ -->
+
     <property name="DaemonVersion" type="s" access="read">
       <doc:doc><doc:description><doc:para>
             The version of the running daemon.
index d8e8239..13aa4a5 100644 (file)
@@ -55,6 +55,7 @@ content_files =                                               \
        dbus/org.freedesktop.DeviceKit.Disks.ref.xml            \
        dbus/org.freedesktop.DeviceKit.Disks.Device.ref.xml     \
        dbus/org.freedesktop.DeviceKit.Disks.Controller.ref.xml \
+       dbus/org.freedesktop.DeviceKit.Disks.Port.ref.xml       \
        $(NULL)
 
 # Images to copy into HTML directory
index 13c6144..da5ba59 100644 (file)
@@ -1,5 +1,5 @@
 
-all : org.freedesktop.DeviceKit.Disks.ref.xml org.freedesktop.DeviceKit.Disks.Device.ref.xml org.freedesktop.DeviceKit.Disks.Controller.ref.xml
+all : org.freedesktop.DeviceKit.Disks.ref.xml org.freedesktop.DeviceKit.Disks.Device.ref.xml org.freedesktop.DeviceKit.Disks.Controller.ref.xml org.freedesktop.DeviceKit.Disks.Port.ref.xml
 
 org.freedesktop.DeviceKit.Disks.ref.xml : $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.xml $(top_srcdir)/doc/dbus/spec-to-docbook.xsl
        echo "<?xml version=\"1.0\"?>""<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.1.2//EN\" \"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd\">" > $@
@@ -13,6 +13,10 @@ org.freedesktop.DeviceKit.Disks.Controller.ref.xml : $(top_srcdir)/data/org.free
        echo "<?xml version=\"1.0\"?>""<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.1.2//EN\" \"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd\">" > $@
        $(XSLTPROC) -nonet $(top_srcdir)/doc/dbus/spec-to-docbook.xsl $< | tail -n +2 >> $@
 
+org.freedesktop.DeviceKit.Disks.Port.ref.xml : $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.Port.xml $(top_srcdir)/doc/dbus/spec-to-docbook.xsl
+       echo "<?xml version=\"1.0\"?>""<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.1.2//EN\" \"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd\">" > $@
+       $(XSLTPROC) -nonet $(top_srcdir)/doc/dbus/spec-to-docbook.xsl $< | tail -n +2 >> $@
+
 EXTRA_DIST = spec-to-docbook.xsl dbus-introspect-docs.dtd
 
 clean-local :
index bd5b0b9..ee2d39c 100644 (file)
@@ -66,6 +66,7 @@
     <xi:include href="dbus/org.freedesktop.DeviceKit.Disks.ref.xml"/>
     <xi:include href="dbus/org.freedesktop.DeviceKit.Disks.Device.ref.xml"/>
     <xi:include href="dbus/org.freedesktop.DeviceKit.Disks.Controller.ref.xml"/>
+    <xi:include href="dbus/org.freedesktop.DeviceKit.Disks.Port.ref.xml"/>
   </reference>
 
   <reference id="tools-fileformats">
index b96904e..73d3252 100644 (file)
@@ -22,6 +22,7 @@ BUILT_SOURCES =                                                       \
        devkit-disks-daemon-glue.h                              \
        devkit-disks-device-glue.h                              \
        devkit-disks-controller-glue.h                          \
+       devkit-disks-port-glue.h                                \
        devkit-disks-marshal.h          devkit-disks-marshal.c
 
 devkit-disks-marshal.h: devkit-disks-marshal.list
@@ -39,6 +40,9 @@ devkit-disks-device-glue.h: $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.D
 devkit-disks-controller-glue.h: $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.Controller.xml Makefile.am
        dbus-binding-tool --prefix=devkit_disks_controller --mode=glib-server --output=devkit-disks-controller-glue.h $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.Controller.xml
 
+devkit-disks-port-glue.h: $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.Port.xml Makefile.am
+       dbus-binding-tool --prefix=devkit_disks_port --mode=glib-server --output=devkit-disks-port-glue.h $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.Port.xml
+
 libexec_PROGRAMS = devkit-disks-daemon
 
 devkit_disks_daemon_SOURCES =                                                          \
@@ -49,6 +53,8 @@ devkit_disks_daemon_SOURCES =                                                                 \
        devkit-disks-device-private.h           devkit-disks-device-private.c           \
        devkit-disks-controller.h               devkit-disks-controller.c               \
        devkit-disks-controller-private.h       devkit-disks-controller-private.c       \
+       devkit-disks-port.h                     devkit-disks-port.c                     \
+       devkit-disks-port-private.h             devkit-disks-port-private.c             \
        devkit-disks-mount-file.h               devkit-disks-mount-file.c               \
        devkit-disks-mount.h                    devkit-disks-mount.c                    \
        devkit-disks-mount-monitor.h            devkit-disks-mount-monitor.c            \
index 40a5ec0..12621e4 100644 (file)
@@ -96,3 +96,24 @@ devkit_disks_controller_set_driver (DevkitDisksController *controller, const gch
       emit_changed (controller, "driver");
     }
 }
+
+void
+devkit_disks_controller_set_num_ports (DevkitDisksController *controller, guint value)
+{
+  if (G_UNLIKELY (controller->priv->num_ports != value))
+    {
+      controller->priv->num_ports = value;
+      emit_changed (controller, "num_ports");
+    }
+}
+
+void
+devkit_disks_controller_set_fabric (DevkitDisksController *controller, const gchar *value)
+{
+  if (G_UNLIKELY (g_strcmp0 (controller->priv->fabric, value) != 0))
+    {
+      g_free (controller->priv->fabric);
+      controller->priv->fabric = g_strdup (value);
+      emit_changed (controller, "fabric");
+    }
+}
index e2e4b64..6fd582c 100644 (file)
@@ -49,6 +49,8 @@ struct DevkitDisksControllerPrivate
         gchar *vendor;
         gchar *model;
         gchar *driver;
+        guint num_ports;
+        gchar *fabric;
 };
 
 /* property setters */
@@ -56,6 +58,8 @@ struct DevkitDisksControllerPrivate
 void devkit_disks_controller_set_vendor (DevkitDisksController *controller, const gchar *value);
 void devkit_disks_controller_set_model (DevkitDisksController *controller, const gchar *value);
 void devkit_disks_controller_set_driver (DevkitDisksController *controller, const gchar *value);
+void devkit_disks_controller_set_num_ports (DevkitDisksController *controller, guint value);
+void devkit_disks_controller_set_fabric (DevkitDisksController *controller, const gchar *value);
 
 G_END_DECLS
 
index 26c5c26..2280897 100644 (file)
@@ -56,6 +56,8 @@ enum
         PROP_VENDOR,
         PROP_MODEL,
         PROP_DRIVER,
+        PROP_NUM_PORTS,
+        PROP_FABRIC,
 };
 
 enum
@@ -95,6 +97,14 @@ get_property (GObject         *object,
                 g_value_set_string (value, controller->priv->driver);
                 break;
 
+        case PROP_NUM_PORTS:
+                g_value_set_uint (value, controller->priv->num_ports);
+                break;
+
+        case PROP_FABRIC:
+                g_value_set_string (value, controller->priv->fabric);
+                break;
+
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -138,6 +148,14 @@ devkit_disks_controller_class_init (DevkitDisksControllerClass *klass)
                 object_class,
                 PROP_DRIVER,
                 g_param_spec_string ("driver", NULL, NULL, NULL, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_NUM_PORTS,
+                g_param_spec_uint ("num-ports", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_FABRIC,
+                g_param_spec_string ("fabric", NULL, NULL, NULL, G_PARAM_READABLE));
 }
 
 static void
index 96e2609..9ec2aed 100644 (file)
@@ -65,6 +65,8 @@
 #include "devkit-disks-device-private.h"
 #include "devkit-disks-controller.h"
 #include "devkit-disks-controller-private.h"
+#include "devkit-disks-port.h"
+#include "devkit-disks-port-private.h"
 #include "devkit-disks-mount-file.h"
 #include "devkit-disks-mount.h"
 #include "devkit-disks-mount-monitor.h"
@@ -95,6 +97,9 @@ enum
         CONTROLLER_ADDED_SIGNAL,
         CONTROLLER_REMOVED_SIGNAL,
         CONTROLLER_CHANGED_SIGNAL,
+        PORT_ADDED_SIGNAL,
+        PORT_REMOVED_SIGNAL,
+        PORT_CHANGED_SIGNAL,
         LAST_SIGNAL,
 };
 
@@ -119,6 +124,9 @@ struct DevkitDisksDaemonPrivate
         GHashTable              *map_native_path_to_controller;
         GHashTable              *map_object_path_to_controller;
 
+        GHashTable              *map_native_path_to_port;
+        GHashTable              *map_object_path_to_port;
+
         DevkitDisksMountMonitor *mount_monitor;
 
         guint                    ata_smart_refresh_timer_id;
@@ -533,6 +541,33 @@ devkit_disks_daemon_class_init (DevkitDisksDaemonClass *klass)
                               g_cclosure_marshal_VOID__BOXED,
                               G_TYPE_NONE, 1, DBUS_TYPE_G_OBJECT_PATH);
 
+        signals[PORT_ADDED_SIGNAL] =
+                g_signal_new ("port-added",
+                              G_OBJECT_CLASS_TYPE (klass),
+                              G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                              0,
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__BOXED,
+                              G_TYPE_NONE, 1, DBUS_TYPE_G_OBJECT_PATH);
+
+        signals[PORT_REMOVED_SIGNAL] =
+                g_signal_new ("port-removed",
+                              G_OBJECT_CLASS_TYPE (klass),
+                              G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                              0,
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__BOXED,
+                              G_TYPE_NONE, 1, DBUS_TYPE_G_OBJECT_PATH);
+
+        signals[PORT_CHANGED_SIGNAL] =
+                g_signal_new ("port-changed",
+                              G_OBJECT_CLASS_TYPE (klass),
+                              G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                              0,
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__BOXED,
+                              G_TYPE_NONE, 1, DBUS_TYPE_G_OBJECT_PATH);
+
 
         dbus_g_object_type_install_info (DEVKIT_DISKS_TYPE_DAEMON, &dbus_glib_devkit_disks_daemon_object_info);
 
@@ -591,6 +626,14 @@ devkit_disks_daemon_init (DevkitDisksDaemon *daemon)
                                                                              g_str_equal,
                                                                              g_free,
                                                                              g_object_unref);
+        daemon->priv->map_native_path_to_port = g_hash_table_new_full (g_str_hash,
+                                                                       g_str_equal,
+                                                                       g_free,
+                                                                       g_object_unref);
+        daemon->priv->map_object_path_to_port = g_hash_table_new_full (g_str_hash,
+                                                                       g_str_equal,
+                                                                       g_free,
+                                                                       g_object_unref);
 }
 
 static void
@@ -636,6 +679,12 @@ devkit_disks_daemon_finalize (GObject *object)
         if (daemon->priv->map_object_path_to_controller != NULL) {
                 g_hash_table_unref (daemon->priv->map_object_path_to_controller);
         }
+        if (daemon->priv->map_native_path_to_port != NULL) {
+                g_hash_table_unref (daemon->priv->map_native_path_to_port);
+        }
+        if (daemon->priv->map_object_path_to_port != NULL) {
+                g_hash_table_unref (daemon->priv->map_object_path_to_port);
+        }
 
 
         if (daemon->priv->mount_monitor != NULL) {
@@ -709,7 +758,7 @@ pci_device_changed (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean synthesi
         if (controller != NULL) {
                 gboolean keep_controller;
 
-                g_print ("**** CHANGING %s\n", native_path);
+                g_print ("**** pci CHANGING %s\n", native_path);
 
                 /* The sysfs path ('move' uevent) may actually change so remove it and add
                  * it back after processing. The kernel name will never change so the object
@@ -732,17 +781,66 @@ pci_device_changed (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean synthesi
                                      g_object_ref (controller));
 
                 if (!keep_controller) {
-                        g_print ("**** CHANGE TRIGGERED REMOVE %s\n", native_path);
+                        g_print ("**** pci CHANGE TRIGGERED REMOVE %s\n", native_path);
                         device_remove (daemon, d);
                 } else {
-                        g_print ("**** CHANGED %s\n", native_path);
+                        g_print ("**** pci CHANGED %s\n", native_path);
                 }
         } else {
-                g_print ("**** TREATING CHANGE AS ADD %s\n", native_path);
+                g_print ("**** pci TREATING CHANGE AS ADD %s\n", native_path);
+                device_add (daemon, d, TRUE);
+        }
+}
+
+/* ------------------------------ */
+
+static void
+scsi_host_device_changed (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean synthesized)
+{
+        DevkitDisksPort *port;
+        const char *native_path;
+
+        native_path = g_udev_device_get_sysfs_path (d);
+        port = g_hash_table_lookup (daemon->priv->map_native_path_to_port, native_path);
+        if (port != NULL) {
+                gboolean keep_port;
+
+                g_print ("**** scsi_host CHANGING %s\n", native_path);
+
+                /* The sysfs path ('move' uevent) may actually change so remove it and add
+                 * it back after processing. The kernel name will never change so the object
+                 * path will fortunately remain constant.
+                 */
+                g_warn_if_fail (g_hash_table_remove (daemon->priv->map_native_path_to_port,
+                                                     port->priv->native_path));
+
+                keep_port = devkit_disks_port_changed (port, d, synthesized);
+
+                g_assert (devkit_disks_port_local_get_native_path (port) != NULL);
+                g_assert (g_strcmp0 (native_path, devkit_disks_port_local_get_native_path (port)) == 0);
+
+                /* now add the things back to the global hashtables - it's important that we
+                 * do this *before* calling port_remove() - otherwise it will never remove
+                 * the port
+                 */
+                g_hash_table_insert (daemon->priv->map_native_path_to_port,
+                                     g_strdup (devkit_disks_port_local_get_native_path (port)),
+                                     g_object_ref (port));
+
+                if (!keep_port) {
+                        g_print ("**** scsi_host CHANGE TRIGGERED REMOVE %s\n", native_path);
+                        device_remove (daemon, d);
+                } else {
+                        g_print ("**** scsi_host CHANGED %s\n", native_path);
+                }
+        } else {
+                g_print ("**** scsi_host TREATING CHANGE AS ADD %s\n", native_path);
                 device_add (daemon, d, TRUE);
         }
 }
 
+/* ------------------------------ */
+
 static void
 block_device_changed (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean synthesized)
 {
@@ -807,6 +905,8 @@ device_changed (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean synthesized)
                 block_device_changed (daemon, d, synthesized);
         else if (g_strcmp0 (subsystem, "pci") == 0)
                 pci_device_changed (daemon, d, synthesized);
+        else if (g_strcmp0 (subsystem, "scsi_host") == 0)
+                scsi_host_device_changed (daemon, d, synthesized);
         else
                 g_warning ("Unhandled changed event from subsystem `%s'", subsystem);
 }
@@ -844,10 +944,10 @@ pci_device_add (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean emit_event)
         controller = g_hash_table_lookup (daemon->priv->map_native_path_to_controller, native_path);
         if (controller != NULL) {
                 /* we already have the controller; treat as change event */
-                g_print ("**** TREATING ADD AS CHANGE %s\n", native_path);
+                g_print ("**** pci TREATING ADD AS CHANGE %s\n", native_path);
                 device_changed (daemon, d, FALSE);
         } else {
-                g_print ("**** ADDING %s\n", native_path);
+                g_print ("**** pci ADDING %s\n", native_path);
                 controller = devkit_disks_controller_new (daemon, d);
 
                 if (controller != NULL) {
@@ -862,19 +962,64 @@ pci_device_add (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean emit_event)
                         g_hash_table_insert (daemon->priv->map_object_path_to_controller,
                                              g_strdup (devkit_disks_controller_local_get_object_path (controller)),
                                              g_object_ref (controller));
-                        g_print ("**** ADDED %s\n", native_path);
+                        g_print ("**** pci ADDED %s\n", native_path);
                         if (emit_event) {
                                 const char *object_path;
                                 object_path = devkit_disks_controller_local_get_object_path (controller);
-                                g_print ("**** EMITTING ADDED for %s\n", controller->priv->native_path);
+                                g_print ("**** pci EMITTING ADDED for %s\n", controller->priv->native_path);
                                 g_signal_emit (daemon, signals[CONTROLLER_ADDED_SIGNAL], 0, object_path);
                         }
                 } else {
-                        g_print ("**** IGNORING ADD %s\n", native_path);
+                        g_print ("**** pci IGNORING ADD %s\n", native_path);
                 }
         }
 }
 
+/* ------------------------------ */
+
+static void
+scsi_host_device_add (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean emit_event)
+{
+        DevkitDisksPort *port;
+        const char *native_path;
+
+        native_path = g_udev_device_get_sysfs_path (d);
+        port = g_hash_table_lookup (daemon->priv->map_native_path_to_port, native_path);
+        if (port != NULL) {
+                /* we already have the port; treat as change event */
+                g_print ("**** scsi_host TREATING ADD AS CHANGE %s\n", native_path);
+                device_changed (daemon, d, FALSE);
+        } else {
+                g_print ("**** scsi_host ADDING %s\n", native_path);
+                port = devkit_disks_port_new (daemon, d);
+
+                if (port != NULL) {
+                        /* assert that the port is fully loaded with info */
+                        g_assert (devkit_disks_port_local_get_native_path (port) != NULL);
+                        g_assert (devkit_disks_port_local_get_object_path (port) != NULL);
+                        g_assert (g_strcmp0 (native_path, devkit_disks_port_local_get_native_path (port)) == 0);
+
+                        g_hash_table_insert (daemon->priv->map_native_path_to_port,
+                                             g_strdup (devkit_disks_port_local_get_native_path (port)),
+                                             g_object_ref (port));
+                        g_hash_table_insert (daemon->priv->map_object_path_to_port,
+                                             g_strdup (devkit_disks_port_local_get_object_path (port)),
+                                             g_object_ref (port));
+                        g_print ("**** scsi_host ADDED %s\n", native_path);
+                        if (emit_event) {
+                                const char *object_path;
+                                object_path = devkit_disks_port_local_get_object_path (port);
+                                g_print ("**** scsi_host EMITTING ADDED for %s\n", port->priv->native_path);
+                                g_signal_emit (daemon, signals[PORT_ADDED_SIGNAL], 0, object_path);
+                        }
+                } else {
+                        g_print ("**** scsi_host IGNORING ADD %s\n", native_path);
+                }
+        }
+}
+
+/* ------------------------------ */
+
 static void
 block_device_add (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean emit_event)
 {
@@ -935,6 +1080,8 @@ device_add (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean emit_event)
                 block_device_add (daemon, d, emit_event);
         else if (g_strcmp0 (subsystem, "pci") == 0)
                 pci_device_add (daemon, d, emit_event);
+        else if (g_strcmp0 (subsystem, "scsi_host") == 0)
+                scsi_host_device_add (daemon, d, emit_event);
         else
                 g_warning ("Unhandled add event from subsystem `%s'", subsystem);
 }
@@ -950,9 +1097,9 @@ pci_device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d)
         native_path = g_udev_device_get_sysfs_path (d);
         controller = g_hash_table_lookup (daemon->priv->map_native_path_to_controller, native_path);
         if (controller == NULL) {
-                g_print ("**** IGNORING REMOVE %s\n", native_path);
+                g_print ("**** pci IGNORING REMOVE %s\n", native_path);
         } else {
-                g_print ("**** REMOVING %s\n", native_path);
+                g_print ("**** pci REMOVING %s\n", native_path);
 
                 g_warn_if_fail (g_strcmp0 (native_path, controller->priv->native_path) == 0);
 
@@ -961,7 +1108,7 @@ pci_device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d)
                 g_warn_if_fail (g_hash_table_remove (daemon->priv->map_object_path_to_controller,
                                                      controller->priv->object_path));
 
-                g_print ("**** EMITTING REMOVED for %s\n", controller->priv->native_path);
+                g_print ("**** pci EMITTING REMOVED for %s\n", controller->priv->native_path);
                 g_signal_emit (daemon, signals[CONTROLLER_REMOVED_SIGNAL], 0,
                                devkit_disks_controller_local_get_object_path (controller));
 
@@ -971,6 +1118,40 @@ pci_device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d)
         }
 }
 
+/* ------------------------------ */
+
+static void
+scsi_host_device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d)
+{
+        DevkitDisksPort *port;
+        const char *native_path;
+
+        native_path = g_udev_device_get_sysfs_path (d);
+        port = g_hash_table_lookup (daemon->priv->map_native_path_to_port, native_path);
+        if (port == NULL) {
+                g_print ("**** scsi_host IGNORING REMOVE %s\n", native_path);
+        } else {
+                g_print ("**** scsi_host REMOVING %s\n", native_path);
+
+                g_warn_if_fail (g_strcmp0 (native_path, port->priv->native_path) == 0);
+
+                g_hash_table_remove (daemon->priv->map_native_path_to_port,
+                                     port->priv->native_path);
+                g_warn_if_fail (g_hash_table_remove (daemon->priv->map_object_path_to_port,
+                                                     port->priv->object_path));
+
+                g_print ("**** scsi_host EMITTING REMOVED for %s\n", port->priv->native_path);
+                g_signal_emit (daemon, signals[PORT_REMOVED_SIGNAL], 0,
+                               devkit_disks_port_local_get_object_path (port));
+
+                devkit_disks_port_removed (port);
+
+                g_object_unref (port);
+        }
+}
+
+/* ------------------------------ */
+
 static void
 block_device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d)
 {
@@ -1026,6 +1207,8 @@ device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d)
                 block_device_remove (daemon, d);
         else if (g_strcmp0 (subsystem, "pci") == 0)
                 pci_device_remove (daemon, d);
+        else if (g_strcmp0 (subsystem, "scsi_host") == 0)
+                scsi_host_device_remove (daemon, d);
         else
                 g_warning ("Unhandled remove event from subsystem `%s'", subsystem);
 }
@@ -1194,7 +1377,10 @@ register_disks_daemon (DevkitDisksDaemon *daemon)
         DBusConnection *connection;
         DBusError dbus_error;
         GError *error = NULL;
-        const char *subsystems[] = {"block", "pci", NULL};
+        const char *subsystems[] = {"block",      /* Disks and partitions */
+                                    "pci",        /* Storage controllers */
+                                    "scsi_host",  /* ATA ports are represented by scsi_host */
+                                    NULL};
 
         daemon->priv->authority = polkit_authority_get ();
 
@@ -1290,7 +1476,7 @@ devkit_disks_daemon_new (void)
                 goto error;
         }
 
-        /* process controllers */
+        /* process storage controllers */
         devices = g_udev_client_query_by_subsystem (daemon->priv->gudev_client, "pci");
         for (l = devices; l != NULL; l = l->next) {
                 GUdevDevice *device = l->data;
@@ -1299,7 +1485,16 @@ devkit_disks_daemon_new (void)
         g_list_foreach (devices, (GFunc) g_object_unref, NULL);
         g_list_free (devices);
 
-        /* process block devices */
+        /* process ATA ports */
+        devices = g_udev_client_query_by_subsystem (daemon->priv->gudev_client, "scsi_host");
+        for (l = devices; l != NULL; l = l->next) {
+                GUdevDevice *device = l->data;
+                device_add (daemon, device, FALSE);
+        }
+        g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+        g_list_free (devices);
+
+        /* process block devices (disks and partitions) */
         devices = g_udev_client_query_by_subsystem (daemon->priv->gudev_client, "block");
         for (l = devices; l != NULL; l = l->next) {
                 GUdevDevice *device = l->data;
@@ -1837,7 +2032,7 @@ devkit_disks_daemon_local_update_spindown (DevkitDisksDaemon *daemon)
 
 DevkitDisksController *
 devkit_disks_daemon_local_find_controller (DevkitDisksDaemon       *daemon,
-                                           const gchar             *child_native_path)
+                                           const gchar             *device_native_path)
 {
         GHashTableIter iter;
         const gchar *native_path;
@@ -1848,7 +2043,7 @@ devkit_disks_daemon_local_find_controller (DevkitDisksDaemon       *daemon,
 
         g_hash_table_iter_init (&iter, daemon->priv->map_native_path_to_controller);
         while (g_hash_table_iter_next (&iter, (gpointer) &native_path, (gpointer) &controller)) {
-                if (g_str_has_prefix (child_native_path,
+                if (g_str_has_prefix (device_native_path,
                                       devkit_disks_controller_local_get_native_path (controller))) {
                         ret = controller;
                         break;
@@ -1858,6 +2053,29 @@ devkit_disks_daemon_local_find_controller (DevkitDisksDaemon       *daemon,
         return ret;
 }
 
+DevkitDisksPort *
+devkit_disks_daemon_local_find_port (DevkitDisksDaemon       *daemon,
+                                     const gchar             *device_native_path)
+{
+        GHashTableIter iter;
+        const gchar *native_path;
+        DevkitDisksPort *port;
+        DevkitDisksPort *ret;
+
+        ret = NULL;
+
+        g_hash_table_iter_init (&iter, daemon->priv->map_native_path_to_port);
+        while (g_hash_table_iter_next (&iter, (gpointer) &native_path, (gpointer) &port)) {
+
+                if (devkit_disks_local_port_is_for_device (port, device_native_path)) {
+                        ret = port;
+                        break;
+                }
+        }
+
+        return ret;
+}
+
 /*--------------------------------------------------------------------------------------------------------------*/
 /* exported methods */
 
@@ -1917,6 +2135,32 @@ devkit_disks_daemon_enumerate_controllers (DevkitDisksDaemon     *daemon,
 /*--------------------------------------------------------------------------------------------------------------*/
 
 static void
+enumerate_port_cb (gpointer key, gpointer value, gpointer user_data)
+{
+        DevkitDisksPort *port = DEVKIT_DISKS_PORT (value);
+        GPtrArray *object_paths = user_data;
+        g_ptr_array_add (object_paths, g_strdup (devkit_disks_port_local_get_object_path (port)));
+}
+
+/* dbus-send --system --print-reply --dest=org.freedesktop.DeviceKit.Disks /org/freedesktop/DeviceKit/Disks org.freedesktop.DeviceKit.Disks.EnumeratePorts
+ */
+gboolean
+devkit_disks_daemon_enumerate_ports (DevkitDisksDaemon     *daemon,
+                                           DBusGMethodInvocation *context)
+{
+        GPtrArray *object_paths;
+
+        object_paths = g_ptr_array_new ();
+        g_hash_table_foreach (daemon->priv->map_native_path_to_port, enumerate_port_cb, object_paths);
+        dbus_g_method_return (context, object_paths);
+        g_ptr_array_foreach (object_paths, (GFunc) g_free, NULL);
+        g_ptr_array_free (object_paths, TRUE);
+        return TRUE;
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+static void
 enumerate_device_files_cb (gpointer key, gpointer value, gpointer user_data)
 {
         DevkitDisksDevice *device = DEVKIT_DISKS_DEVICE (value);
index 95e15e0..3e6748a 100644 (file)
@@ -91,7 +91,9 @@ DevkitDisksDevice *devkit_disks_daemon_local_find_by_dev         (DevkitDisksDae
                                                                   dev_t                    dev);
 
 DevkitDisksController *devkit_disks_daemon_local_find_controller (DevkitDisksDaemon       *daemon,
-                                                                  const gchar             *child_native_path);
+                                                                  const gchar             *device_native_path);
+DevkitDisksPort *devkit_disks_daemon_local_find_port (DevkitDisksDaemon       *daemon,
+                                                      const gchar             *device_native_path);
 
 typedef void (*DevkitDisksCheckAuthCallback) (DevkitDisksDaemon     *daemon,
                                               DevkitDisksDevice     *device,
@@ -157,6 +159,9 @@ const DevkitDisksFilesystem *devkit_disks_daemon_local_get_fs_details (DevkitDis
 gboolean devkit_disks_daemon_enumerate_controllers (DevkitDisksDaemon     *daemon,
                                                     DBusGMethodInvocation *context);
 
+gboolean devkit_disks_daemon_enumerate_ports (DevkitDisksDaemon     *daemon,
+                                              DBusGMethodInvocation *context);
+
 gboolean devkit_disks_daemon_enumerate_devices (DevkitDisksDaemon     *daemon,
                                                 DBusGMethodInvocation *context);
 
index 4f1bdaf..3e1eb9c 100644 (file)
@@ -806,6 +806,17 @@ devkit_disks_device_set_drive_controller (DevkitDisksDevice *device, const gchar
 }
 
 void
+devkit_disks_device_set_drive_port (DevkitDisksDevice *device, const gchar *value)
+{
+  if (G_UNLIKELY (g_strcmp0 (device->priv->drive_port, value) != 0))
+    {
+      g_free (device->priv->drive_port);
+      device->priv->drive_port = g_strdup (value);
+      emit_changed (device, "drive_port");
+    }
+}
+
+void
 devkit_disks_device_set_optical_disc_is_blank (DevkitDisksDevice *device, gboolean value)
 {
   if (G_UNLIKELY (device->priv->optical_disc_is_blank != value))
index 6c9611e..1e7b3bc 100644 (file)
@@ -160,6 +160,7 @@ struct DevkitDisksDevicePrivate
         guint drive_rotation_rate;
         char *drive_write_cache;
         char *drive_controller;
+        char *drive_port;
 
         gboolean optical_disc_is_blank;
         gboolean optical_disc_is_appendable;
@@ -283,6 +284,7 @@ void devkit_disks_device_set_drive_is_rotational (DevkitDisksDevice *device, gbo
 void devkit_disks_device_set_drive_rotation_rate (DevkitDisksDevice *device, guint value);
 void devkit_disks_device_set_drive_write_cache (DevkitDisksDevice *device, const gchar *value);
 void devkit_disks_device_set_drive_controller (DevkitDisksDevice *device, const gchar *value);
+void devkit_disks_device_set_drive_port (DevkitDisksDevice *device, const gchar *value);
 
 void devkit_disks_device_set_optical_disc_is_blank (DevkitDisksDevice *device, gboolean value);
 void devkit_disks_device_set_optical_disc_is_appendable (DevkitDisksDevice *device, gboolean value);
index 038841d..f2789b8 100644 (file)
@@ -60,6 +60,7 @@
 #include "devkit-disks-inhibitor.h"
 #include "devkit-disks-poller.h"
 #include "devkit-disks-controller.h"
+#include "devkit-disks-port.h"
 
 /*--------------------------------------------------------------------------------------------------------------*/
 #include "devkit-disks-device-glue.h"
@@ -218,6 +219,7 @@ enum
         PROP_DRIVE_ROTATION_RATE,
         PROP_DRIVE_WRITE_CACHE,
         PROP_DRIVE_CONTROLLER,
+        PROP_DRIVE_PORT,
 
         PROP_OPTICAL_DISC_IS_BLANK,
         PROP_OPTICAL_DISC_IS_APPENDABLE,
@@ -533,6 +535,12 @@ get_property (GObject         *object,
                 else
                         g_value_set_boxed (value, "/");
                break;
+       case PROP_DRIVE_PORT:
+                if (device->priv->drive_port != NULL)
+                        g_value_set_boxed (value, device->priv->drive_port);
+                else
+                        g_value_set_boxed (value, "/");
+               break;
 
        case PROP_OPTICAL_DISC_IS_BLANK:
                g_value_set_boolean (value, device->priv->optical_disc_is_blank);
@@ -1003,6 +1011,10 @@ devkit_disks_device_class_init (DevkitDisksDeviceClass *klass)
                 object_class,
                 PROP_DRIVE_CONTROLLER,
                 g_param_spec_boxed ("drive-controller", NULL, NULL, DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_PORT,
+                g_param_spec_boxed ("drive-port", NULL, NULL, DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE));
 
         g_object_class_install_property (
                 object_class,
@@ -1239,6 +1251,7 @@ devkit_disks_device_finalize (GObject *object)
         g_free (device->priv->drive_media);
         g_free (device->priv->drive_write_cache);
         g_free (device->priv->drive_controller);
+        g_free (device->priv->drive_port);
 
         g_free (device->priv->linux_md_component_level);
         g_free (device->priv->linux_md_component_uuid);
@@ -2928,9 +2941,9 @@ update_info_media_detection (DevkitDisksDevice *device)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-/* driver_controller property */
+/* drive_controller property */
 static gboolean
-update_info_driver_controller (DevkitDisksDevice *device)
+update_info_drive_controller (DevkitDisksDevice *device)
 {
         DevkitDisksController *controller;
         const gchar *controller_object_path;
@@ -2948,6 +2961,26 @@ update_info_driver_controller (DevkitDisksDevice *device)
         return TRUE;
 }
 
+/* drive_port property */
+static gboolean
+update_info_drive_port (DevkitDisksDevice *device)
+{
+        DevkitDisksPort *port;
+        const gchar *port_object_path;
+
+        port_object_path = NULL;
+
+        port = devkit_disks_daemon_local_find_port (device->priv->daemon,
+                                                    device->priv->native_path);
+        if (port != NULL) {
+                port_object_path = devkit_disks_port_local_get_object_path (port);
+        }
+
+        devkit_disks_device_set_drive_port (device, port_object_path);
+
+        return TRUE;
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 typedef struct {
@@ -3342,8 +3375,12 @@ update_info (DevkitDisksDevice *device)
         if (!update_info_media_detection (device))
                 goto out;
 
-        /* device_controller proprety */
-        if (!update_info_driver_controller (device))
+        /* drive_controller proprety */
+        if (!update_info_drive_controller (device))
+                goto out;
+
+        /* drive_port proprety */
+        if (!update_info_drive_port (device))
                 goto out;
 
         ret = TRUE;
diff --git a/src/devkit-disks-port-private.c b/src/devkit-disks-port-private.c
new file mode 100644 (file)
index 0000000..f02edec
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2009 David Zeuthen <david@fubar.dk>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <string.h>
+#include "devkit-disks-port.h"
+#include "devkit-disks-port-private.h"
+
+static gboolean
+emit_changed_idle_cb (gpointer data)
+{
+  DevkitDisksPort *port = DEVKIT_DISKS_PORT (data);
+
+  //g_debug ("XXX emitting 'changed' in idle");
+
+  if (!port->priv->removed)
+    {
+      g_print ("**** EMITTING CHANGED for %s\n", port->priv->native_path);
+      g_signal_emit_by_name (port->priv->daemon,
+                             "port-changed",
+                             port->priv->object_path);
+      g_signal_emit_by_name (port, "changed");
+    }
+  port->priv->emit_changed_idle_id = 0;
+
+  /* remove the idle source */
+  return FALSE;
+}
+
+static void
+emit_changed (DevkitDisksPort *port, const gchar *name)
+{
+  //g_debug ("property %s changed for %s", name, port->priv->port_file);
+
+  if (port->priv->object_path != NULL)
+    {
+      /* schedule a 'changed' signal in idle if one hasn't been scheduled already */
+      if (port->priv->emit_changed_idle_id == 0)
+        {
+          port->priv->emit_changed_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT,
+                                                                    emit_changed_idle_cb,
+                                                                    g_object_ref (port),
+                                                                    (GDestroyNotify) g_object_unref);
+        }
+    }
+}
+
+void
+devkit_disks_port_set_controller (DevkitDisksPort *port, const gchar *value)
+{
+  if (G_UNLIKELY (g_strcmp0 (port->priv->controller, value) != 0))
+    {
+      g_free (port->priv->controller);
+      port->priv->controller = g_strdup (value);
+      emit_changed (port, "controller");
+    }
+}
+
+void
+devkit_disks_port_set_parent (DevkitDisksPort *port, const gchar *value)
+{
+  if (G_UNLIKELY (g_strcmp0 (port->priv->parent, value) != 0))
+    {
+      g_free (port->priv->parent);
+      port->priv->parent = g_strdup (value);
+      emit_changed (port, "parent");
+    }
+}
+
+void
+devkit_disks_port_set_number (DevkitDisksPort *port, gint value)
+{
+  if (G_UNLIKELY (port->priv->number != value))
+    {
+       port->priv->number = value;
+      emit_changed (port, "number");
+    }
+}
diff --git a/src/devkit-disks-port-private.h b/src/devkit-disks-port-private.h
new file mode 100644 (file)
index 0000000..f53d8d9
--- /dev/null
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 David Zeuthen <david@fubar.dk>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __DEVKIT_DISKS_PORT_PRIVATE_H__
+#define __DEVKIT_DISKS_PORT_PRIVATE_H__
+
+#include <dbus/dbus-glib.h>
+#include <gudev/gudev.h>
+#include <atasmart.h>
+
+#include "devkit-disks-types.h"
+
+G_BEGIN_DECLS
+
+struct DevkitDisksPortPrivate
+{
+        DBusGConnection *system_bus_connection;
+        DevkitDisksDaemon *daemon;
+        GUdevDevice *d;
+
+        gchar *object_path;
+        gchar *native_path;
+        gboolean removed;
+
+        /* if non-zero, the id of the idle for emitting a 'change' signal */
+        guint emit_changed_idle_id;
+
+        gchar *native_path_for_device_prefix;
+
+        /**************/
+        /* Properties */
+        /**************/
+
+        gchar *controller;
+        gchar *parent;
+        gint number;
+};
+
+/* property setters */
+
+void devkit_disks_port_set_controller (DevkitDisksPort *port, const gchar *value);
+void devkit_disks_port_set_parent (DevkitDisksPort *port, const gchar *value);
+void devkit_disks_port_set_number (DevkitDisksPort *port, gint value);
+
+G_END_DECLS
+
+#endif /* __DEVKIT_DISKS_PORT_PRIVATE_H__ */
diff --git a/src/devkit-disks-port.c b/src/devkit-disks-port.c
new file mode 100644 (file)
index 0000000..943b26c
--- /dev/null
@@ -0,0 +1,587 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 David Zeuthen <david@fubar.dk>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
+#include <glib-object.h>
+#include <gio/gunixmounts.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <gudev/gudev.h>
+#include <stdlib.h>
+
+#include "devkit-disks-daemon.h"
+#include "devkit-disks-port.h"
+#include "devkit-disks-port-private.h"
+#include "devkit-disks-marshal.h"
+
+#include "devkit-disks-controller.h"
+
+/*--------------------------------------------------------------------------------------------------------------*/
+#include "devkit-disks-port-glue.h"
+
+static void     devkit_disks_port_class_init  (DevkitDisksPortClass *klass);
+static void     devkit_disks_port_init        (DevkitDisksPort      *seat);
+static void     devkit_disks_port_finalize    (GObject     *object);
+
+static gboolean update_info                (DevkitDisksPort *port);
+
+static void     drain_pending_changes (DevkitDisksPort *port, gboolean force_update);
+
+enum
+{
+        PROP_0,
+        PROP_NATIVE_PATH,
+
+        PROP_CONTROLLER,
+        PROP_PARENT,
+        PROP_NUMBER,
+};
+
+enum
+{
+        CHANGED_SIGNAL,
+        LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (DevkitDisksPort, devkit_disks_port, G_TYPE_OBJECT)
+
+#define DEVKIT_DISKS_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEVKIT_DISKS_TYPE_PORT, DevkitDisksPortPrivate))
+
+static void
+get_property (GObject         *object,
+              guint            prop_id,
+              GValue          *value,
+              GParamSpec      *pspec)
+{
+        DevkitDisksPort *port = DEVKIT_DISKS_PORT (object);
+
+        switch (prop_id) {
+        case PROP_NATIVE_PATH:
+                g_value_set_string (value, port->priv->native_path);
+                break;
+
+        case PROP_CONTROLLER:
+                if (port->priv->controller != NULL)
+                        g_value_set_boxed (value, port->priv->controller);
+                else
+                        g_value_set_boxed (value, "/");
+                break;
+
+        case PROP_PARENT:
+                if (port->priv->parent != NULL)
+                        g_value_set_boxed (value, port->priv->parent);
+                else
+                        g_value_set_boxed (value, "/");
+                break;
+
+        case PROP_NUMBER:
+                g_value_set_int (value, port->priv->number);
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+devkit_disks_port_class_init (DevkitDisksPortClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = devkit_disks_port_finalize;
+        object_class->get_property = get_property;
+
+        g_type_class_add_private (klass, sizeof (DevkitDisksPortPrivate));
+
+        signals[CHANGED_SIGNAL] =
+                g_signal_new ("changed",
+                              G_OBJECT_CLASS_TYPE (klass),
+                              G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                              0,
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        dbus_g_object_type_install_info (DEVKIT_DISKS_TYPE_PORT, &dbus_glib_devkit_disks_port_object_info);
+
+        g_object_class_install_property (
+                object_class,
+                PROP_NATIVE_PATH,
+                g_param_spec_string ("native-path", NULL, NULL, NULL, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_CONTROLLER,
+                g_param_spec_boxed ("controller", NULL, NULL, DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_PARENT,
+                g_param_spec_boxed ("parent", NULL, NULL, DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_NUMBER,
+                g_param_spec_int ("number", NULL, NULL, G_MININT, G_MAXINT, -1, G_PARAM_READABLE));
+}
+
+static void
+devkit_disks_port_init (DevkitDisksPort *port)
+{
+        port->priv = DEVKIT_DISKS_PORT_GET_PRIVATE (port);
+        port->priv->number = -1;
+}
+
+static void
+devkit_disks_port_finalize (GObject *object)
+{
+        DevkitDisksPort *port;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (DEVKIT_DISKS_IS_PORT (object));
+
+        port = DEVKIT_DISKS_PORT (object);
+        g_return_if_fail (port->priv != NULL);
+
+        /* g_debug ("finalizing %s", port->priv->native_path); */
+
+        g_object_unref (port->priv->d);
+        g_object_unref (port->priv->daemon);
+        g_free (port->priv->object_path);
+
+        g_free (port->priv->native_path);
+        g_free (port->priv->native_path_for_device_prefix);
+
+        if (port->priv->emit_changed_idle_id > 0)
+                g_source_remove (port->priv->emit_changed_idle_id);
+
+        /* free properties */
+        g_free (port->priv->controller);
+        g_free (port->priv->parent);
+
+        G_OBJECT_CLASS (devkit_disks_port_parent_class)->finalize (object);
+}
+
+/**
+ * compute_object_path:
+ * @port: A #DevkitDisksPort.
+ *
+ * Computes the D-Bus object path for the port.
+ *
+ * Returns: A valid D-Bus object path. Free with g_free().
+ */
+static char *
+compute_object_path (DevkitDisksPort *port)
+{
+        const gchar *basename;
+        GString *s;
+        guint n;
+
+        basename = strrchr (port->priv->native_path, '/');
+        if (basename != NULL) {
+                basename++;
+        } else {
+                basename = port->priv->native_path;
+        }
+
+        s = g_string_new (port->priv->parent);
+        g_string_append_c (s, '/');
+        for (n = 0; basename[n] != '\0'; n++) {
+                gint c = basename[n];
+
+                /* D-Bus spec sez:
+                 *
+                 * Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_"
+                 */
+                if ((c >= 'A' && c <= 'Z') ||
+                    (c >= 'a' && c <= 'z') ||
+                    (c >= '0' && c <= '9')) {
+                        g_string_append_c (s, c);
+                } else {
+                        /* Escape bytes not in [A-Z][a-z][0-9] as _<hex-with-two-digits> */
+                        g_string_append_printf (s, "_%02x", c);
+                }
+        }
+
+        return g_string_free (s, FALSE);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+register_disks_port (DevkitDisksPort *port)
+{
+        DBusConnection *connection;
+        GError *error = NULL;
+
+        port->priv->system_bus_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+        if (port->priv->system_bus_connection == NULL) {
+                if (error != NULL) {
+                        g_critical ("error getting system bus: %s", error->message);
+                        g_error_free (error);
+                }
+                goto error;
+        }
+        connection = dbus_g_connection_get_connection (port->priv->system_bus_connection);
+
+        port->priv->object_path = compute_object_path (port);
+
+        /* safety first */
+        if (dbus_g_connection_lookup_g_object (port->priv->system_bus_connection,
+                                               port->priv->object_path) != NULL) {
+                g_error ("**** HACK: Wanting to register object at path `%s' but there is already an "
+                         "object there. This is an internal error in the daemon. Aborting.\n",
+                         port->priv->object_path);
+        }
+
+        dbus_g_connection_register_g_object (port->priv->system_bus_connection,
+                                             port->priv->object_path,
+                                             G_OBJECT (port));
+
+        return TRUE;
+
+error:
+        return FALSE;
+}
+
+void
+devkit_disks_port_removed (DevkitDisksPort *port)
+{
+        port->priv->removed = TRUE;
+
+        dbus_g_connection_unregister_g_object (port->priv->system_bus_connection,
+                                               G_OBJECT (port));
+        g_assert (dbus_g_connection_lookup_g_object (port->priv->system_bus_connection,
+                                                     port->priv->object_path) == NULL);
+}
+
+DevkitDisksPort *
+devkit_disks_port_new (DevkitDisksDaemon *daemon, GUdevDevice *d)
+{
+        DevkitDisksPort *port;
+        const char *native_path;
+
+        port = NULL;
+        native_path = g_udev_device_get_sysfs_path (d);
+
+        port = DEVKIT_DISKS_PORT (g_object_new (DEVKIT_DISKS_TYPE_PORT, NULL));
+        port->priv->d = g_object_ref (d);
+        port->priv->daemon = g_object_ref (daemon);
+        port->priv->native_path = g_strdup (native_path);
+
+        if (!update_info (port)) {
+                g_object_unref (port);
+                port = NULL;
+                goto out;
+        }
+
+        if (!register_disks_port (DEVKIT_DISKS_PORT (port))) {
+                g_object_unref (port);
+                port = NULL;
+                goto out;
+        }
+
+out:
+        return port;
+}
+
+static void
+drain_pending_changes (DevkitDisksPort *port, gboolean force_update)
+{
+        gboolean emit_changed;
+
+        emit_changed = FALSE;
+
+        /* the update-in-idle is set up if, and only if, there are pending changes - so
+         * we should emit a 'change' event only if it is set up
+         */
+        if (port->priv->emit_changed_idle_id != 0) {
+                g_source_remove (port->priv->emit_changed_idle_id);
+                port->priv->emit_changed_idle_id = 0;
+                emit_changed = TRUE;
+        }
+
+        if ((!port->priv->removed) && (emit_changed || force_update)) {
+                if (port->priv->object_path != NULL) {
+                        g_print ("**** EMITTING CHANGED for %s\n", port->priv->native_path);
+                        g_signal_emit_by_name (port, "changed");
+                        g_signal_emit_by_name (port->priv->daemon, "port-changed", port->priv->object_path);
+                }
+        }
+}
+
+/* called by the daemon on the 'change' uevent */
+gboolean
+devkit_disks_port_changed (DevkitDisksPort *port, GUdevDevice *d, gboolean synthesized)
+{
+        gboolean keep_port;
+
+        g_object_unref (port->priv->d);
+        port->priv->d = g_object_ref (d);
+
+        keep_port = update_info (port);
+
+        /* this 'change' event might prompt us to remove the port */
+        if (!keep_port)
+                goto out;
+
+        /* no, it's good .. keep it.. and always force a 'change' signal if the event isn't synthesized */
+        drain_pending_changes (port, !synthesized);
+
+out:
+        return keep_port;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+const char *
+devkit_disks_port_local_get_object_path (DevkitDisksPort *port)
+{
+        return port->priv->object_path;
+}
+
+const char *
+devkit_disks_port_local_get_native_path (DevkitDisksPort *port)
+{
+        return port->priv->native_path;
+}
+
+gboolean
+devkit_disks_local_port_is_for_device (DevkitDisksPort *port,
+                                       const gchar     *device_native_path)
+{
+        return g_str_has_prefix (device_native_path, port->priv->native_path_for_device_prefix);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static char *
+sysfs_resolve_link (const char *sysfs_path, const char *name)
+{
+        char *full_path;
+        char link_path[PATH_MAX];
+        char resolved_path[PATH_MAX];
+        ssize_t num;
+        gboolean found_it;
+
+        found_it = FALSE;
+
+        full_path = g_build_filename (sysfs_path, name, NULL);
+
+        //g_debug ("name='%s'", name);
+        //g_debug ("full_path='%s'", full_path);
+        num = readlink (full_path, link_path, sizeof (link_path) - 1);
+        if (num != -1) {
+                char *absolute_path;
+
+                link_path[num] = '\0';
+
+                //g_debug ("link_path='%s'", link_path);
+                absolute_path = g_build_filename (sysfs_path, link_path, NULL);
+                //g_debug ("absolute_path='%s'", absolute_path);
+                if (realpath (absolute_path, resolved_path) != NULL) {
+                        //g_debug ("resolved_path='%s'", resolved_path);
+                        found_it = TRUE;
+                }
+                g_free (absolute_path);
+        }
+        g_free (full_path);
+
+        if (found_it)
+                return g_strdup (resolved_path);
+        else
+                return NULL;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gint
+int_compare_func (gconstpointer a,
+                  gconstpointer b)
+{
+        gint a_val;
+        gint b_val;
+
+        a_val = *((gint *) a);
+        b_val = *((gint *) b);
+
+        return a_val - b_val;
+}
+
+/* Update info for an ATA port */
+static gboolean
+update_info_ata (DevkitDisksPort       *port,
+                 DevkitDisksController *controller)
+{
+        GDir *dir;
+        GError *error;
+        gboolean ret;
+        const gchar *name;
+        GArray *numbers;
+        guint n;
+        const gchar *basename;
+        gint port_host_number;
+        gint port_number;
+
+        ret = FALSE;
+        port_number = -1;
+        dir = NULL;
+        numbers = NULL;
+
+        /* First, figure out prefix used for matching the device on the port */
+        if (port->priv->native_path_for_device_prefix == NULL) {
+                port->priv->native_path_for_device_prefix =
+                        sysfs_resolve_link (g_udev_device_get_sysfs_path (port->priv->d),
+                                            "device");
+                if (port->priv->native_path_for_device_prefix == NULL) {
+                        g_warning ("Unable to resolve 'device' symlink for %s",
+                                   g_udev_device_get_sysfs_path (port->priv->d));
+                        goto out;
+                }
+        }
+
+        /* Second, figure out the port number.
+         *
+         * As ATA drivers create one scsi_host objects for each port
+         * the port number can be inferred from the numbering of the
+         * scsi_host objects.
+         *
+         * We also want to check that the scsi_host object *really*
+         * stems from SATA. There doesn't seem to be an easy way of
+         * doing this so the heuristic used right now is that it
+         * the scsi_host is ATA if, and only if, there is more than
+         * one scsi_host....
+         *
+         * (TODO: we could match on driver names instead? Anyway,
+         *        there are plans to properly support the ATA
+         *        topology (including PMP) once libata switches
+         *        away from SCSI. Once this is available these
+         *        hacks can go away...)
+         */
+
+        basename = strrchr (port->priv->native_path, '/');
+        if (basename == NULL || sscanf (basename + 1, "host%d", &port_host_number) != 1) {
+                g_warning ("Cannot extract port host number from %s", port->priv->native_path);
+                goto out;
+        }
+
+        dir = g_dir_open (devkit_disks_controller_local_get_native_path (controller),
+                          0,
+                          &error);
+        if (dir == NULL) {
+                g_warning ("Unable to open %s: %s",
+                           devkit_disks_controller_local_get_native_path (controller),
+                           error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        numbers = g_array_new (FALSE, FALSE, sizeof (gint));
+
+        while ((name = g_dir_read_name (dir)) != NULL) {
+                gint number;
+
+                if (sscanf (name, "host%d", &number) != 1)
+                        continue;
+
+                g_array_append_val (numbers, number);
+        }
+
+        if (numbers->len < 2) {
+                /* This is (probably, see above) ATA since there isn't at least two
+                 * scsi_host objects.
+                 */
+                goto out;
+        }
+
+        g_array_sort (numbers, int_compare_func);
+
+        for (n = 0; n < numbers->len; n++) {
+                gint number;
+
+                number = ((gint *) numbers->data) [n];
+
+                if (number == port_host_number) {
+                        port_number = n;
+                        break;
+                }
+        }
+
+        devkit_disks_port_set_number (port, port_number);
+        ret = TRUE;
+
+ out:
+        if (dir != NULL)
+                g_dir_close (dir);
+        if (numbers != NULL)
+                g_array_unref (numbers);
+
+        return ret;
+}
+
+/**
+ * update_info:
+ * @port: the port
+ *
+ * Update information about the port.
+ *
+ * If one or more properties changed, the changes are scheduled to be emitted. Use
+ * drain_pending_changes() to force emitting the pending changes (which is useful
+ * before returning the result of an operation).
+ *
+ * Returns: #TRUE to keep (or add) the port; #FALSE to ignore (or remove) the port
+ **/
+static gboolean
+update_info (DevkitDisksPort *port)
+{
+        gboolean ret;
+        DevkitDisksController *controller;
+
+        ret = FALSE;
+
+        controller = devkit_disks_daemon_local_find_controller (port->priv->daemon,
+                                                                port->priv->native_path);
+        if (controller == NULL)
+                goto out;
+
+        /* TODO: handle expansion devices here (SAS expanders, SATA Port Multipliers) */
+        devkit_disks_port_set_controller (port, devkit_disks_controller_local_get_object_path (controller));
+        devkit_disks_port_set_parent (port, devkit_disks_controller_local_get_object_path (controller));
+
+        if (g_strcmp0 (g_udev_device_get_subsystem (port->priv->d), "scsi_host") == 0) {
+                /* TODO: ensure this is really an ATA port - bail if it's not */
+                if (!update_info_ata (port, controller))
+                        goto out;
+        } else {
+                goto out;
+        }
+
+        ret = TRUE;
+
+ out:
+        return ret;
+}
+
diff --git a/src/devkit-disks-port.h b/src/devkit-disks-port.h
new file mode 100644 (file)
index 0000000..394154b
--- /dev/null
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 David Zeuthen <david@fubar.dk>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __DEVKIT_DISKS_PORT_H__
+#define __DEVKIT_DISKS_PORT_H__
+
+#include <dbus/dbus-glib.h>
+#include <gudev/gudev.h>
+#include <sys/types.h>
+
+#include "devkit-disks-types.h"
+
+G_BEGIN_DECLS
+
+#define DEVKIT_DISKS_TYPE_PORT         (devkit_disks_port_get_type ())
+#define DEVKIT_DISKS_PORT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DEVKIT_DISKS_TYPE_PORT, DevkitDisksPort))
+#define DEVKIT_DISKS_PORT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), DEVKIT_DISKS_TYPE_PORT, DevkitDisksPortClass))
+#define DEVKIT_DISKS_IS_PORT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DEVKIT_DISKS_TYPE_PORT))
+#define DEVKIT_DISKS_IS_PORT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), DEVKIT_DISKS_TYPE_PORT))
+#define DEVKIT_DISKS_PORT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DEVKIT_DISKS_TYPE_PORT, DevkitDisksPortClass))
+
+typedef struct DevkitDisksPortClass   DevkitDisksPortClass;
+typedef struct DevkitDisksPortPrivate DevkitDisksPortPrivate;
+
+struct DevkitDisksPort
+{
+        GObject                 parent;
+        DevkitDisksPortPrivate *priv;
+};
+
+struct DevkitDisksPortClass
+{
+        GObjectClass parent_class;
+};
+
+GType              devkit_disks_port_get_type              (void) G_GNUC_CONST;
+
+DevkitDisksPort *devkit_disks_port_new               (DevkitDisksDaemon     *daemon,
+                                                      GUdevDevice           *d);
+
+gboolean           devkit_disks_port_changed         (DevkitDisksPort *port,
+                                                      GUdevDevice     *d,
+                                                      gboolean         synthesized);
+
+void               devkit_disks_port_removed         (DevkitDisksPort *port);
+
+/* local methods */
+
+const char *devkit_disks_port_local_get_object_path (DevkitDisksPort *port);
+const char *devkit_disks_port_local_get_native_path (DevkitDisksPort *port);
+gboolean    devkit_disks_local_port_is_for_device   (DevkitDisksPort *port,
+                                                     const gchar     *device_native_path);
+
+G_END_DECLS
+
+#endif /* __DEVKIT_DISKS_PORT_H__ */
index bce8de8..4a7d676 100644 (file)
 G_BEGIN_DECLS
 
 typedef struct DevkitDisksDaemon       DevkitDisksDaemon;
+
 typedef struct DevkitDisksDevice       DevkitDisksDevice;
 typedef struct DevkitDisksController   DevkitDisksController;
+typedef struct DevkitDisksPort         DevkitDisksPort;
+
 typedef struct DevkitDisksMount        DevkitDisksMount;
 typedef struct DevkitDisksMountMonitor DevkitDisksMountMonitor;
 typedef struct DevkitDisksInhibitor    DevkitDisksInhibitor;
index 36caba0..abc336f 100644 (file)
@@ -371,6 +371,7 @@ typedef struct
         guint    drive_rotation_rate;
         char    *drive_write_cache;
         char    *drive_controller;
+        char    *drive_port;
 
         gboolean optical_disc_is_blank;
         gboolean optical_disc_is_appendable;
@@ -568,6 +569,8 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props)
                 props->drive_write_cache = g_strdup (g_value_get_string (value));
         else if (strcmp (key, "DriveController") == 0)
                 props->drive_controller = g_strdup (g_value_get_boxed (value));
+        else if (strcmp (key, "DrivePort") == 0)
+                props->drive_port = g_strdup (g_value_get_boxed (value));
 
         else if (strcmp (key, "OpticalDiscIsBlank") == 0)
                 props->optical_disc_is_blank = g_value_get_boolean (value);
@@ -689,6 +692,7 @@ device_properties_free (DeviceProperties *props)
         g_free (props->drive_media);
         g_free (props->drive_write_cache);
         g_free (props->drive_controller);
+        g_free (props->drive_port);
 
         g_free (props->drive_ata_smart_status);
         g_free (props->drive_ata_smart_blob);
@@ -1129,7 +1133,8 @@ do_show_info (const char *object_path)
                         g_print ("    write-cache:               %s\n", props->drive_write_cache);
                 }
                 g_print ("    ejectable:                 %d\n", props->drive_is_media_ejectable);
-                g_print ("    controller:                %s\n", props->drive_controller != NULL ? props->drive_controller : "Unknown");
+                g_print ("    controller:                %s\n", strlen (props->drive_controller) > 1 ? props->drive_controller : "Unknown");
+                g_print ("    port:                      %s\n", strlen (props->drive_port) > 1 ? props->drive_port : "Unknown");
                 g_print ("    media:                     %s\n", props->drive_media);
                 g_print ("      compat:                 ");
                 for (n = 0; props->drive_media_compatibility[n] != NULL; n++)