summaryrefslogtreecommitdiff
path: root/monitor
diff options
context:
space:
mode:
authorPhilip Langdale <philipl@overt.org>2013-01-11 20:34:25 -0800
committerPhilip Langdale <philipl@overt.org>2013-01-11 20:34:25 -0800
commit55ef4a2a84731f6e87f9d5983ef8473269df4f65 (patch)
tree7dad96be030335b9fcd77f8019809f3aab9782e6 /monitor
parent4cd213d4db7c77de6788cb8d790babcc0dd6daa0 (diff)
parent66015d2669f4f393ab9fb0fbf734c6f137a56cb4 (diff)
downloadgvfs-55ef4a2a84731f6e87f9d5983ef8473269df4f65.tar.gz
Merge branch 'mtp-backend'
This merge brings in the libmtp based backend for MTP devices. Previously, MTP devices were handled, with limited success by the GPhoto2 backend as MTP is nominally backwards compatible with PTP. The most serious limitation was that the GPhoto2 backend operated in a way that doesn't work reliably with Android 4.x based devices. This problem is rectified by the new MTP backend.
Diffstat (limited to 'monitor')
-rw-r--r--monitor/Makefile.am6
-rw-r--r--monitor/gphoto2/ggphoto2volumemonitor.c7
-rw-r--r--monitor/mtp/.gitignore1
-rw-r--r--monitor/mtp/Makefile.am51
-rw-r--r--monitor/mtp/gmtpvolume.c433
-rw-r--r--monitor/mtp/gmtpvolume.h59
-rw-r--r--monitor/mtp/gmtpvolumemonitor.c330
-rw-r--r--monitor/mtp/gmtpvolumemonitor.h53
-rw-r--r--monitor/mtp/mtp-volume-monitor-daemon.c36
-rw-r--r--monitor/mtp/mtp.monitor4
-rw-r--r--monitor/mtp/org.gtk.Private.MTPVolumeMonitor.service.in3
11 files changed, 982 insertions, 1 deletions
diff --git a/monitor/Makefile.am b/monitor/Makefile.am
index d903df1f..7a4e87fc 100644
--- a/monitor/Makefile.am
+++ b/monitor/Makefile.am
@@ -1,4 +1,4 @@
-DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2
+DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2 mtp
SUBDIRS = proxy
if USE_HAL
@@ -20,3 +20,7 @@ endif
if USE_AFC
SUBDIRS += afc
endif
+
+if USE_LIBMTP
+SUBDIRS += mtp
+endif
diff --git a/monitor/gphoto2/ggphoto2volumemonitor.c b/monitor/gphoto2/ggphoto2volumemonitor.c
index 64ef3838..24811d45 100644
--- a/monitor/gphoto2/ggphoto2volumemonitor.c
+++ b/monitor/gphoto2/ggphoto2volumemonitor.c
@@ -201,6 +201,13 @@ gudev_add_camera (GGPhoto2VolumeMonitor *monitor, GUdevDevice *device, gboolean
return;
}
#endif /* HAVE_AFC */
+#ifdef HAVE_LIBMTP
+ if (g_udev_device_get_property_as_boolean (device, "ID_MTP_DEVICE"))
+ {
+ /* g_debug ("ignoring device, is MTP"); */
+ return;
+ }
+#endif /* HAVE_LIBMTP */
usb_bus_num = g_udev_device_get_property (device, "BUSNUM");
if (usb_bus_num == NULL) {
diff --git a/monitor/mtp/.gitignore b/monitor/mtp/.gitignore
new file mode 100644
index 00000000..0bf331b1
--- /dev/null
+++ b/monitor/mtp/.gitignore
@@ -0,0 +1 @@
+gvfs-mtp-volume-monitor
diff --git a/monitor/mtp/Makefile.am b/monitor/mtp/Makefile.am
new file mode 100644
index 00000000..2f585c65
--- /dev/null
+++ b/monitor/mtp/Makefile.am
@@ -0,0 +1,51 @@
+
+NULL =
+
+libexec_PROGRAMS = gvfs-mtp-volume-monitor
+
+gvfs_mtp_volume_monitor_SOURCES =
+
+gvfs_mtp_volume_monitor_SOURCES += \
+ mtp-volume-monitor-daemon.c \
+ gmtpvolume.c gmtpvolume.h \
+ gmtpvolumemonitor.c gmtpvolumemonitor.h \
+ $(NULL)
+
+gvfs_mtp_volume_monitor_CFLAGS = \
+ -DG_LOG_DOMAIN=\"GVFS-MTP\" \
+ -I$(top_srcdir)/common \
+ -I$(top_srcdir)/monitor/proxy \
+ $(GLIB_CFLAGS) \
+ -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \
+ -DGVFS_LOCALEDIR=\""$(localedir)"\" \
+ -DG_UDEV_API_IS_SUBJECT_TO_CHANGE \
+ $(NULL)
+
+gvfs_mtp_volume_monitor_CFLAGS += $(GUDEV_CFLAGS)
+
+gvfs_mtp_volume_monitor_LDFLAGS = \
+ $(NULL)
+
+gvfs_mtp_volume_monitor_LDADD = \
+ $(GLIB_LIBS) \
+ $(top_builddir)/common/libgvfscommon.la \
+ $(top_builddir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la \
+ $(NULL)
+
+gvfs_mtp_volume_monitor_LDADD += $(GUDEV_LIBS)
+
+
+remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors
+remote_volume_monitors_DATA = mtp.monitor
+
+servicedir = $(datadir)/dbus-1/services
+service_in_files = org.gtk.Private.MTPVolumeMonitor.service.in
+service_DATA = $(service_in_files:.service.in=.service)
+
+$(service_DATA): $(service_in_files) Makefile
+ $(AM_V_GEN) $(SED) -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
+
+clean-local:
+ rm -f *~ *.loT $(service_DATA)
+
+EXTRA_DIST = $(service_in_files) mtp.monitor
diff --git a/monitor/mtp/gmtpvolume.c b/monitor/mtp/gmtpvolume.c
new file mode 100644
index 00000000..07d8674a
--- /dev/null
+++ b/monitor/mtp/gmtpvolume.c
@@ -0,0 +1,433 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * Volume Monitor for MTP Backend
+ *
+ * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
+ * - Based on ggphoto2volume.c
+ * - Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include "gmtpvolume.h"
+
+G_LOCK_DEFINE_STATIC (mtp_volume);
+
+struct _GMtpVolume {
+ GObject parent;
+
+ GVolumeMonitor *volume_monitor; /* owned by volume monitor */
+
+ char *device_path;
+ GUdevDevice *device;
+
+ GFile *activation_root;
+
+ char *name;
+ char *icon;
+};
+
+static void g_mtp_volume_volume_iface_init (GVolumeIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GMtpVolume, g_mtp_volume, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME,
+ g_mtp_volume_volume_iface_init))
+
+static void
+g_mtp_volume_finalize (GObject *object)
+{
+ GMtpVolume *volume;
+
+ volume = G_MTP_VOLUME (object);
+
+ g_clear_object (&volume->device);
+ g_clear_object (&volume->activation_root);
+
+ if (volume->volume_monitor != NULL)
+ g_object_remove_weak_pointer (G_OBJECT (volume->volume_monitor), (gpointer) &(volume->volume_monitor));
+
+ g_free (volume->name);
+ g_free (volume->icon);
+
+ (*G_OBJECT_CLASS (g_mtp_volume_parent_class)->finalize) (object);
+}
+
+static void
+g_mtp_volume_class_init (GMtpVolumeClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = g_mtp_volume_finalize;
+}
+
+static void
+g_mtp_volume_init (GMtpVolume *mtp_volume)
+{
+}
+
+static int hexdigit (char c)
+{
+ if (c >= 'a')
+ return c - 'a' + 10;
+ if (c >= 'A')
+ return c - 'A' + 10;
+ g_return_val_if_fail (c >= '0' && c <= '9', 0);
+ return c - '0';
+}
+
+/* Do not free result, it's a static buffer */
+static const char*
+udev_decode_string (const char* encoded)
+{
+ int len;
+ const char* s;
+ char *decoded;
+
+ if (encoded == NULL)
+ return NULL;
+
+ decoded = g_malloc(4096);
+
+ for (len = 0, s = encoded; *s && len < sizeof (decoded) - 1; ++len, ++s) {
+ /* need to check for NUL terminator in advance */
+ if (s[0] == '\\' && s[1] == 'x' && s[2] >= '0' && s[3] >= '0') {
+ decoded[len] = (hexdigit (s[2]) << 4) | hexdigit (s[3]);
+ s += 3;
+ } else {
+ decoded[len] = *s;
+ }
+ }
+ decoded[len] = '\0';
+ return decoded;
+}
+
+static void
+set_volume_name (GMtpVolume *v)
+{
+ const char *gphoto_name;
+ const char *product = NULL;
+ const char *vendor;
+ const char *model;
+
+ /* our preference: ID_MTP > ID_MEDIA_PLAYER_{VENDOR,PRODUCT} > product >
+ * ID_{VENDOR,MODEL} */
+
+ gphoto_name = g_udev_device_get_property (v->device, "ID_MTP");
+ if (gphoto_name != NULL && strcmp (gphoto_name, "1") != 0) {
+ v->name = g_strdup (gphoto_name);
+ return;
+ }
+
+ vendor = g_udev_device_get_property (v->device, "ID_MEDIA_PLAYER_VENDOR");
+ if (vendor == NULL)
+ vendor = g_udev_device_get_property (v->device, "ID_VENDOR_ENC");
+ model = g_udev_device_get_property (v->device, "ID_MEDIA_PLAYER_MODEL");
+ if (model == NULL) {
+ model = g_udev_device_get_property (v->device, "ID_MODEL_ENC");
+ product = g_udev_device_get_sysfs_attr (v->device, "product");
+ }
+
+ v->name = NULL;
+ if (product != NULL && strlen (product) > 0) {
+ v->name = g_strdup (product);
+ } else if (vendor == NULL) {
+ if (model != NULL)
+ v->name = g_strdup (udev_decode_string (model));
+ } else {
+ if (model != NULL) {
+ /* we can't call udev_decode_string() twice in one g_strdup_printf(),
+ * it returns a static buffer */
+ gchar *temp = g_strconcat (vendor, " ", model, NULL);
+ v->name = g_strdup (udev_decode_string (temp));
+ g_free (temp);
+ } else {
+ if (g_udev_device_has_property (v->device, "ID_MEDIA_PLAYER")) {
+ /* Translators: %s is the device vendor */
+ v->name = g_strdup_printf (_("%s Audio Player"), udev_decode_string (vendor));
+ } else {
+ /* Translators: %s is the device vendor */
+ v->name = g_strdup_printf (_("%s Camera"), udev_decode_string (vendor));
+ }
+ }
+ }
+
+ if (v->name == NULL)
+ v->name = g_strdup (_("Camera"));
+}
+
+static void
+set_volume_icon (GMtpVolume *volume)
+{
+ if (g_udev_device_has_property (volume->device, "ID_MEDIA_PLAYER_ICON_NAME"))
+ volume->icon = g_strdup (g_udev_device_get_property (volume->device, "ID_MEDIA_PLAYER_ICON_NAME"));
+ else if (g_udev_device_has_property (volume->device, "ID_MEDIA_PLAYER"))
+ volume->icon = g_strdup ("multimedia-player");
+ else
+ volume->icon = g_strdup ("camera-photo");
+}
+
+GMtpVolume *
+g_mtp_volume_new (GVolumeMonitor *volume_monitor,
+ GUdevDevice *device,
+ GUdevClient *gudev_client,
+ GFile *activation_root)
+{
+ GMtpVolume *volume;
+ const char *device_path;
+
+ g_return_val_if_fail (volume_monitor != NULL, NULL);
+ g_return_val_if_fail (device != NULL, NULL);
+ g_return_val_if_fail (gudev_client != NULL, NULL);
+ g_return_val_if_fail (activation_root != NULL, NULL);
+
+ if (!g_udev_device_has_property (device, "ID_MTP_DEVICE"))
+ return NULL;
+ device_path = g_udev_device_get_device_file (device);
+
+ volume = g_object_new (G_TYPE_MTP_VOLUME, NULL);
+ volume->volume_monitor = volume_monitor;
+ g_object_add_weak_pointer (G_OBJECT (volume_monitor), (gpointer) &(volume->volume_monitor));
+ volume->device_path = g_strdup (device_path);
+ volume->device = g_object_ref (device);
+ volume->activation_root = g_object_ref (activation_root);
+
+ set_volume_name (volume);
+ set_volume_icon (volume);
+ /* we do not really need to listen for changes */
+
+ return volume;
+}
+
+void
+g_mtp_volume_removed (GMtpVolume *volume)
+{
+}
+
+static GIcon *
+g_mtp_volume_get_icon (GVolume *volume)
+{
+ GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
+ GIcon *icon;
+
+ G_LOCK (mtp_volume);
+ icon = g_themed_icon_new (mtp_volume->icon);
+ G_UNLOCK (mtp_volume);
+ return icon;
+}
+
+static char *
+g_mtp_volume_get_name (GVolume *volume)
+{
+ GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
+ char *name;
+
+ G_LOCK (mtp_volume);
+ name = g_strdup (mtp_volume->name);
+ G_UNLOCK (mtp_volume);
+
+ return name;
+}
+
+static char *
+g_mtp_volume_get_uuid (GVolume *volume)
+{
+ return NULL;
+}
+
+static gboolean
+g_mtp_volume_can_mount (GVolume *volume)
+{
+ return TRUE;
+}
+
+static gboolean
+g_mtp_volume_can_eject (GVolume *volume)
+{
+ return FALSE;
+}
+
+static gboolean
+g_mtp_volume_should_automount (GVolume *volume)
+{
+ return TRUE;
+}
+
+static GDrive *
+g_mtp_volume_get_drive (GVolume *volume)
+{
+ return NULL;
+}
+
+static GMount *
+g_mtp_volume_get_mount (GVolume *volume)
+{
+ return NULL;
+}
+
+gboolean
+g_mtp_volume_has_path (GMtpVolume *volume,
+ const char *sysfs_path)
+{
+ GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
+ gboolean res;
+
+ G_LOCK (mtp_volume);
+ res = FALSE;
+ if (mtp_volume->device != NULL)
+ res = strcmp (g_udev_device_get_sysfs_path (mtp_volume->device), sysfs_path) == 0;
+ G_UNLOCK (mtp_volume);
+ return res;
+}
+
+typedef struct
+{
+ GMtpVolume *enclosing_volume;
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+} ActivationMountOp;
+
+static void
+mount_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ ActivationMountOp *data = user_data;
+ data->callback (G_OBJECT (data->enclosing_volume), res, data->user_data);
+ g_free (data);
+}
+
+static void
+g_mtp_volume_mount (GVolume *volume,
+ GMountMountFlags flags,
+ GMountOperation *mount_operation,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
+ ActivationMountOp *data;
+
+ /*g_warning ("mtp_volume_mount (can_mount=%d foreign=%p device_path=%s)",
+ g_mtp_volume_can_mount (volume),
+ mtp_volume->activation_root,
+ mtp_volume->device_path);*/
+
+ G_LOCK (mtp_volume);
+
+ data = g_new0 (ActivationMountOp, 1);
+ data->enclosing_volume = mtp_volume;
+ data->callback = callback;
+ data->user_data = user_data;
+
+ g_file_mount_enclosing_volume (mtp_volume->activation_root,
+ 0,
+ mount_operation,
+ cancellable,
+ mount_callback,
+ data);
+
+ G_UNLOCK (mtp_volume);
+}
+
+static gboolean
+g_mtp_volume_mount_finish (GVolume *volume,
+ GAsyncResult *result,
+ GError **error)
+{
+ GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
+ gboolean res;
+
+ G_LOCK (mtp_volume);
+ res = g_file_mount_enclosing_volume_finish (mtp_volume->activation_root, result, error);
+ G_UNLOCK (mtp_volume);
+
+ return res;
+}
+
+static char *
+g_mtp_volume_get_identifier (GVolume *volume,
+ const char *kind)
+{
+ GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
+ char *id;
+
+ G_LOCK (mtp_volume);
+ id = NULL;
+ if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == 0)
+ id = g_strdup (mtp_volume->device_path);
+ G_UNLOCK (mtp_volume);
+
+ return id;
+}
+
+static char **
+g_mtp_volume_enumerate_identifiers (GVolume *volume)
+{
+ GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
+ GPtrArray *res;
+
+ G_LOCK (mtp_volume);
+
+ res = g_ptr_array_new ();
+
+ if (mtp_volume->device_path && *mtp_volume->device_path != 0)
+ g_ptr_array_add (res,
+ g_strdup (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE));
+
+ /* Null-terminate */
+ g_ptr_array_add (res, NULL);
+
+ G_UNLOCK (mtp_volume);
+
+ return (char **)g_ptr_array_free (res, FALSE);
+}
+
+static GFile *
+g_mtp_volume_get_activation_root (GVolume *volume)
+{
+ GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
+
+ return g_object_ref (mtp_volume->activation_root);
+}
+
+static void
+g_mtp_volume_volume_iface_init (GVolumeIface *iface)
+{
+ iface->get_name = g_mtp_volume_get_name;
+ iface->get_icon = g_mtp_volume_get_icon;
+ iface->get_uuid = g_mtp_volume_get_uuid;
+ iface->get_drive = g_mtp_volume_get_drive;
+ iface->get_mount = g_mtp_volume_get_mount;
+ iface->can_mount = g_mtp_volume_can_mount;
+ iface->can_eject = g_mtp_volume_can_eject;
+ iface->should_automount = g_mtp_volume_should_automount;
+ iface->mount_fn = g_mtp_volume_mount;
+ iface->mount_finish = g_mtp_volume_mount_finish;
+ iface->eject = NULL;
+ iface->eject_finish = NULL;
+ iface->get_identifier = g_mtp_volume_get_identifier;
+ iface->enumerate_identifiers = g_mtp_volume_enumerate_identifiers;
+ iface->get_activation_root = g_mtp_volume_get_activation_root;
+}
diff --git a/monitor/mtp/gmtpvolume.h b/monitor/mtp/gmtpvolume.h
new file mode 100644
index 00000000..a1d2e6b9
--- /dev/null
+++ b/monitor/mtp/gmtpvolume.h
@@ -0,0 +1,59 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * Volume Monitor for MTP Backend
+ *
+ * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_MTP_VOLUME_H__
+#define __G_MTP_VOLUME_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <gudev/gudev.h>
+#include "gmtpvolumemonitor.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_MTP_VOLUME (g_mtp_volume_get_type ())
+#define G_MTP_VOLUME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MTP_VOLUME, GMtpVolume))
+#define G_MTP_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MTP_VOLUME, GMtpVolumeClass))
+#define G_IS_MTP_VOLUME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MTP_VOLUME))
+#define G_IS_MTP_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MTP_VOLUME))
+
+typedef struct _GMtpVolumeClass GMtpVolumeClass;
+
+struct _GMtpVolumeClass {
+ GObjectClass parent_class;
+};
+
+GType g_mtp_volume_get_type (void) G_GNUC_CONST;
+
+GMtpVolume *g_mtp_volume_new (GVolumeMonitor *volume_monitor,
+ GUdevDevice *device,
+ GUdevClient *gudev_client,
+ GFile *activation_root);
+
+gboolean g_mtp_volume_has_path (GMtpVolume *volume,
+ const char *path);
+
+void g_mtp_volume_removed (GMtpVolume *volume);
+
+G_END_DECLS
+
+#endif /* __G_MTP_VOLUME_H__ */
diff --git a/monitor/mtp/gmtpvolumemonitor.c b/monitor/mtp/gmtpvolumemonitor.c
new file mode 100644
index 00000000..bf483fbc
--- /dev/null
+++ b/monitor/mtp/gmtpvolumemonitor.c
@@ -0,0 +1,330 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * Volume Monitor for MTP Backend
+ *
+ * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
+ * - Based on ggphoto2volume.c
+ * - Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include "gmtpvolumemonitor.h"
+#include "gmtpvolume.h"
+
+#include <gio/gunixmounts.h>
+
+G_LOCK_DEFINE_STATIC(vm_lock);
+
+static GMtpVolumeMonitor *the_volume_monitor = NULL;
+
+struct _GMtpVolumeMonitor {
+ GNativeVolumeMonitor parent;
+
+ GUnixMountMonitor *mount_monitor;
+
+ GUdevClient *gudev_client;
+
+ GList *last_devices;
+
+ GList *device_volumes;
+};
+
+static void on_uevent (GUdevClient *client,
+ gchar *action,
+ GUdevDevice *device,
+ gpointer user_data);
+
+G_DEFINE_TYPE (GMtpVolumeMonitor, g_mtp_volume_monitor, G_TYPE_VOLUME_MONITOR)
+
+static void
+list_free (GList *objects)
+{
+ g_list_free_full (objects, g_object_unref);
+}
+
+static void
+g_mtp_volume_monitor_dispose (GObject *object)
+{
+ G_LOCK (vm_lock);
+ the_volume_monitor = NULL;
+ G_UNLOCK (vm_lock);
+
+ (*G_OBJECT_CLASS (g_mtp_volume_monitor_parent_class)->dispose) (object);
+}
+
+static void
+g_mtp_volume_monitor_finalize (GObject *object)
+{
+ GMtpVolumeMonitor *monitor;
+
+ monitor = G_MTP_VOLUME_MONITOR (object);
+
+ g_signal_handlers_disconnect_by_func (monitor->gudev_client, on_uevent, monitor);
+
+ g_object_unref (monitor->gudev_client);
+
+ list_free (monitor->last_devices);
+ list_free (monitor->device_volumes);
+
+ (*G_OBJECT_CLASS (g_mtp_volume_monitor_parent_class)->finalize) (object);
+}
+
+static GList *
+get_mounts (GVolumeMonitor *volume_monitor)
+{
+ return NULL;
+}
+
+static GList *
+get_volumes (GVolumeMonitor *volume_monitor)
+{
+ GMtpVolumeMonitor *monitor;
+ GList *l;
+
+ monitor = G_MTP_VOLUME_MONITOR (volume_monitor);
+
+ G_LOCK (vm_lock);
+
+ l = g_list_copy (monitor->device_volumes);
+ g_list_foreach (l, (GFunc)g_object_ref, NULL);
+
+ G_UNLOCK (vm_lock);
+
+ return l;
+}
+
+static GList *
+get_connected_drives (GVolumeMonitor *volume_monitor)
+{
+ return NULL;
+}
+
+static GVolume *
+get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
+{
+ return NULL;
+}
+
+static GMount *
+get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
+{
+ return NULL;
+}
+
+static void
+gudev_add_device (GMtpVolumeMonitor *monitor, GUdevDevice *device, gboolean do_emit)
+{
+ GMtpVolume *volume;
+ const char *usb_bus_num, *usb_device_num;
+ char *uri;
+ GFile *activation_mount_root;
+
+ usb_bus_num = g_udev_device_get_property (device, "BUSNUM");
+ if (usb_bus_num == NULL) {
+ g_warning ("device %s has no BUSNUM property, ignoring", g_udev_device_get_device_file (device));
+ return;
+ }
+
+ usb_device_num = g_udev_device_get_property (device, "DEVNUM");
+ if (usb_device_num == NULL) {
+ g_warning ("device %s has no DEVNUM property, ignoring", g_udev_device_get_device_file (device));
+ return;
+ }
+
+ g_debug ("gudev_add_device: device %s (bus: %s, device: %s)",
+ g_udev_device_get_device_file (device),
+ usb_bus_num, usb_device_num);
+
+ uri = g_strdup_printf ("mtp://[usb:%s,%s]", usb_bus_num, usb_device_num);
+ activation_mount_root = g_file_new_for_uri (uri);
+ g_free (uri);
+
+ volume = g_mtp_volume_new (G_VOLUME_MONITOR (monitor),
+ device,
+ monitor->gudev_client,
+ activation_mount_root);
+ if (volume != NULL) {
+ monitor->device_volumes = g_list_prepend (monitor->device_volumes, volume);
+ if (do_emit)
+ g_signal_emit_by_name (monitor, "volume_added", volume);
+ }
+
+ if (activation_mount_root != NULL)
+ g_object_unref (activation_mount_root);
+}
+
+static void
+gudev_remove_device (GMtpVolumeMonitor *monitor, GUdevDevice *device)
+{
+ GList *l, *ll;
+ const gchar* sysfs_path;
+
+ sysfs_path = g_udev_device_get_sysfs_path (device);
+
+ g_debug ("gudev_remove_device: %s", g_udev_device_get_device_file (device));
+
+ for (l = monitor->device_volumes; l != NULL; l = ll) {
+ GMtpVolume *volume = G_MTP_VOLUME (l->data);
+
+ ll = l->next;
+
+ if (g_mtp_volume_has_path (volume, sysfs_path)) {
+ g_debug ("gudev_remove_device: found volume %s, deleting", sysfs_path);
+ g_signal_emit_by_name (monitor, "volume_removed", volume);
+ g_signal_emit_by_name (volume, "removed");
+ g_mtp_volume_removed (volume);
+ monitor->device_volumes = g_list_remove (monitor->device_volumes, volume);
+ g_object_unref (volume);
+ }
+ }
+}
+
+static void
+on_uevent (GUdevClient *client, gchar *action, GUdevDevice *device, gpointer user_data)
+{
+ GMtpVolumeMonitor *monitor = G_MTP_VOLUME_MONITOR (user_data);
+
+ g_debug ("on_uevent: action=%s, device=%s", action, g_udev_device_get_device_file(device));
+
+ /* filter out uninteresting events */
+ if (!g_udev_device_has_property (device, "ID_MTP_DEVICE"))
+ {
+ g_debug ("on_uevent: discarding, not ID_MTP");
+ return;
+ }
+
+ if (strcmp (action, "add") == 0)
+ gudev_add_device (monitor, device, TRUE);
+ else if (strcmp (action, "remove") == 0)
+ gudev_remove_device (monitor, device);
+}
+
+static void
+gudev_coldplug_devices (GMtpVolumeMonitor *monitor)
+{
+ GList *usb_devices, *l;
+
+ usb_devices = g_udev_client_query_by_subsystem (monitor->gudev_client, "usb");
+ for (l = usb_devices; l != NULL; l = l->next) {
+ GUdevDevice *d = l->data;
+ if (g_udev_device_has_property (d, "ID_MTP_DEVICE"))
+ gudev_add_device (monitor, d, FALSE);
+ }
+ g_list_free_full(usb_devices, g_object_unref);
+}
+
+static GObject *
+g_mtp_volume_monitor_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GObject *object;
+ GMtpVolumeMonitor *monitor;
+ GMtpVolumeMonitorClass *klass;
+ GObjectClass *parent_class;
+
+ G_LOCK (vm_lock);
+ if (the_volume_monitor != NULL) {
+ object = g_object_ref (the_volume_monitor);
+ G_UNLOCK (vm_lock);
+ return object;
+ }
+ G_UNLOCK (vm_lock);
+
+ /*g_warning ("creating vm singleton");*/
+
+ object = NULL;
+
+ /* Invoke parent constructor. */
+ klass = G_MTP_VOLUME_MONITOR_CLASS (g_type_class_peek (G_TYPE_MTP_VOLUME_MONITOR));
+ parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+ object = parent_class->constructor (type,
+ n_construct_properties,
+ construct_properties);
+
+ monitor = G_MTP_VOLUME_MONITOR (object);
+
+ const char *subsystems[] = { "usb", NULL };
+ monitor->gudev_client = g_udev_client_new (subsystems);
+
+ g_signal_connect (monitor->gudev_client,
+ "uevent", G_CALLBACK (on_uevent),
+ monitor);
+
+ gudev_coldplug_devices (monitor);
+
+ G_LOCK (vm_lock);
+ the_volume_monitor = monitor;
+ G_UNLOCK (vm_lock);
+
+ return object;
+}
+
+static void
+g_mtp_volume_monitor_init (GMtpVolumeMonitor *monitor)
+{
+}
+
+static gboolean
+is_supported (void)
+{
+ /* Today's Linux desktops pretty much need udev to have anything working, so
+ * assume it's there */
+ return TRUE;
+}
+
+static void
+g_mtp_volume_monitor_class_init (GMtpVolumeMonitorClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
+
+ gobject_class->constructor = g_mtp_volume_monitor_constructor;
+ gobject_class->finalize = g_mtp_volume_monitor_finalize;
+ gobject_class->dispose = g_mtp_volume_monitor_dispose;
+
+ monitor_class->get_mounts = get_mounts;
+ monitor_class->get_volumes = get_volumes;
+ monitor_class->get_connected_drives = get_connected_drives;
+ monitor_class->get_volume_for_uuid = get_volume_for_uuid;
+ monitor_class->get_mount_for_uuid = get_mount_for_uuid;
+ monitor_class->is_supported = is_supported;
+}
+
+/**
+ * g_mtp_volume_monitor_new:
+ *
+ * Returns: a new #GVolumeMonitor.
+ **/
+GVolumeMonitor *
+g_mtp_volume_monitor_new (void)
+{
+ GMtpVolumeMonitor *monitor;
+
+ monitor = g_object_new (G_TYPE_MTP_VOLUME_MONITOR, NULL);
+
+ return G_VOLUME_MONITOR (monitor);
+}
diff --git a/monitor/mtp/gmtpvolumemonitor.h b/monitor/mtp/gmtpvolumemonitor.h
new file mode 100644
index 00000000..0a36a9b4
--- /dev/null
+++ b/monitor/mtp/gmtpvolumemonitor.h
@@ -0,0 +1,53 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * Volume Monitor for MTP Backend
+ *
+ * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_MTP_VOLUME_MONITOR_H__
+#define __G_MTP_VOLUME_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_MTP_VOLUME_MONITOR (g_mtp_volume_monitor_get_type ())
+#define G_MTP_VOLUME_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MTP_VOLUME_MONITOR, GMtpVolumeMonitor))
+#define G_MTP_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MTP_VOLUME_MONITOR, GMtpVolumeMonitorClass))
+#define G_IS_MTP_VOLUME_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MTP_VOLUME_MONITOR))
+#define G_IS_MTP_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MTP_VOLUME_MONITOR))
+
+typedef struct _GMtpVolumeMonitor GMtpVolumeMonitor;
+typedef struct _GMtpVolumeMonitorClass GMtpVolumeMonitorClass;
+
+/* Forward definitions */
+typedef struct _GMtpVolume GMtpVolume;
+
+struct _GMtpVolumeMonitorClass {
+ GVolumeMonitorClass parent_class;
+};
+
+GType g_mtp_volume_monitor_get_type (void) G_GNUC_CONST;
+
+GVolumeMonitor *g_mtp_volume_monitor_new (void);
+void g_mtp_volume_monitor_force_update (GMtpVolumeMonitor *monitor);
+
+G_END_DECLS
+
+#endif /* __G_MTP_VOLUME_MONITOR_H__ */
diff --git a/monitor/mtp/mtp-volume-monitor-daemon.c b/monitor/mtp/mtp-volume-monitor-daemon.c
new file mode 100644
index 00000000..20199409
--- /dev/null
+++ b/monitor/mtp/mtp-volume-monitor-daemon.c
@@ -0,0 +1,36 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * Volume Monitor for MTP Backend
+ *
+ * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gvfsproxyvolumemonitordaemon.h>
+
+#include "gmtpvolumemonitor.h"
+
+int
+main (int argc, char *argv[])
+{
+ g_vfs_proxy_volume_monitor_daemon_init ();
+ return g_vfs_proxy_volume_monitor_daemon_main (argc,
+ argv,
+ "org.gtk.Private.MTPVolumeMonitor",
+ G_TYPE_MTP_VOLUME_MONITOR);
+}
diff --git a/monitor/mtp/mtp.monitor b/monitor/mtp/mtp.monitor
new file mode 100644
index 00000000..bfb0c7fe
--- /dev/null
+++ b/monitor/mtp/mtp.monitor
@@ -0,0 +1,4 @@
+[RemoteVolumeMonitor]
+Name=GProxyVolumeMonitorMTP
+DBusName=org.gtk.Private.MTPVolumeMonitor
+IsNative=false
diff --git a/monitor/mtp/org.gtk.Private.MTPVolumeMonitor.service.in b/monitor/mtp/org.gtk.Private.MTPVolumeMonitor.service.in
new file mode 100644
index 00000000..4cd7d190
--- /dev/null
+++ b/monitor/mtp/org.gtk.Private.MTPVolumeMonitor.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gtk.Private.MTPVolumeMonitor
+Exec=@libexecdir@/gvfs-mtp-volume-monitor