summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2008-11-13 16:58:08 +0000
committerRichard Hughes <richard@hughsie.com>2008-11-13 16:58:08 +0000
commita48ce21f83c16d964450763f26592cf6e570650e (patch)
treebbbfd04653c1eb1c433aa6b7912943ec0f602a5d
parentb71c61335a2c5af679feab0d681826506b81180c (diff)
feature: implement the .QoS interface -- still incomplete
-rw-r--r--src/Makefile.am1
-rw-r--r--src/dkp-marshal.list2
-rw-r--r--src/dkp-qos.c525
-rw-r--r--src/dkp-qos.h78
4 files changed, 606 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index d8f57a2..56028fc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -64,6 +64,7 @@ devkit_power_daemon_SOURCES = \
dkp-csr.h dkp-csr.c \
dkp-wup.h dkp-wup.c \
dkp-hid.h dkp-hid.c \
+ dkp-qos.h dkp-qos.c \
dkp-history.h dkp-history.c \
sysfs-utils.h sysfs-utils.c \
main.c \
diff --git a/src/dkp-marshal.list b/src/dkp-marshal.list
index 98218d7..deb5c1d 100644
--- a/src/dkp-marshal.list
+++ b/src/dkp-marshal.list
@@ -1,2 +1,4 @@
VOID:BOOLEAN,STRING,BOOLEAN,INT,INT,STRING,DOUBLE
VOID:STRING,BOOLEAN,STRING,BOOLEAN,INT,INT,STRING,DOUBLE
+VOID:STRING,INT
+
diff --git a/src/dkp-qos.c b/src/dkp-qos.c
new file mode 100644
index 0000000..9dae996
--- /dev/null
+++ b/src/dkp-qos.c
@@ -0,0 +1,525 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+#include "egg-debug.h"
+#include "egg-string.h"
+
+#include "dkp-qos.h"
+#include "dkp-marshal.h"
+#include "dkp-daemon.h"
+#include "dkp-polkit.h"
+
+static void dkp_qos_class_init (DkpQosClass *klass);
+static void dkp_qos_init (DkpQos *qos);
+static void dkp_qos_finalize (GObject *object);
+
+#define DKP_QOS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DKP_TYPE_QOS, DkpQosPrivate))
+
+typedef enum {
+ DKP_QOS_TYPE_NETWORK,
+ DKP_QOS_TYPE_CPU_DMA,
+ DKP_QOS_TYPE_UNKNOWN
+} DkpQosType;
+
+typedef struct
+{
+ gint value;
+ guint uid;
+ guint pid;
+ gchar *sender;
+ guint32 cookie;
+ gboolean persistent;
+ DkpQosType type;
+} DkpQosRequestObj;
+
+struct DkpQosPrivate
+{
+// gint last_network;
+// gint last_cpu_dma;
+// GPtrArray *data_network;
+// GPtrArray *data_cpu_dma;
+ GPtrArray *data;
+ gint last[DKP_QOS_TYPE_UNKNOWN];
+ DkpPolkit *polkit;
+ DBusGConnection *connection;
+ DBusGProxy *proxy;
+};
+
+enum {
+ LATENCY_CHANGED,
+ REQUESTS_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (DkpQos, dkp_qos, G_TYPE_OBJECT)
+
+/**
+ * dkp_qos_type_to_text:
+ **/
+static const gchar *
+dkp_qos_type_to_text (DkpQosType type)
+{
+ if (type == DKP_QOS_TYPE_NETWORK)
+ return "network";
+ if (type == DKP_QOS_TYPE_CPU_DMA)
+ return "cpu_dma";
+ return NULL;
+}
+
+/**
+ * dkp_qos_type_from_text:
+ **/
+static DkpQosType
+dkp_qos_type_from_text (const gchar *type)
+{
+ if (egg_strequal (type, "network"))
+ return DKP_QOS_TYPE_NETWORK;
+ if (egg_strequal (type, "cpu_dma"))
+ return DKP_QOS_TYPE_CPU_DMA;
+ return DKP_QOS_TYPE_UNKNOWN;
+}
+
+/**
+ * dkp_qos_find_from_cookie:
+ **/
+static DkpQosRequestObj *
+dkp_qos_find_from_cookie (DkpQos *qos, guint32 cookie)
+{
+ guint i;
+ GPtrArray *data;
+ DkpQosRequestObj *obj;
+
+ /* search list */
+ data = qos->priv->data;
+ for (i=0; i<data->len; i++) {
+ obj = g_ptr_array_index (data, i);
+ if (obj->cookie == cookie)
+ return obj;
+ }
+
+ /* nothing found */
+ return NULL;
+}
+
+/**
+ * dkp_qos_generate_cookie:
+ *
+ * Return value: a random cookie not already allocated
+ **/
+static guint32
+dkp_qos_generate_cookie (DkpQos *qos)
+{
+ guint32 cookie;
+
+ /* iterate until we have a unique cookie */
+ do {
+ cookie = (guint32) g_random_int_range (1, G_MAXINT32);
+ } while (dkp_qos_find_from_cookie (qos, cookie) != NULL);
+
+ return cookie;
+}
+
+/**
+ * dkp_qos_get_lowest:
+ **/
+static gint
+dkp_qos_get_lowest (DkpQos *qos, DkpQosType type)
+{
+ guint i;
+ gint lowest = G_MAXINT;
+ GPtrArray *data;
+ DkpQosRequestObj *obj;
+
+ /* find lowest */
+ data = qos->priv->data;
+ for (i=0; i<data->len; i++) {
+ obj = g_ptr_array_index (data, i);
+ if (obj->type == type && obj->value > 0 && obj->value < lowest)
+ lowest = obj->value;
+ }
+
+ /* no requests */
+ if (lowest == G_MAXINT)
+ lowest = -1;
+
+ return lowest;
+}
+
+/**
+ * dkp_qos_latency_perhaps_changed:
+ **/
+static gboolean
+dkp_qos_latency_perhaps_changed (DkpQos *qos, DkpQosType type)
+{
+ gint lowest;
+ gint *last;
+
+ /* re-find the lowest value */
+ lowest = dkp_qos_get_lowest (qos, type);
+
+ /* find the last value */
+ last = &qos->priv->last[type];
+
+ /* same value? */
+ if (*last == lowest)
+ return FALSE;
+
+ /* emit signal */
+ g_signal_emit (qos, signals [LATENCY_CHANGED], 0, dkp_qos_type_to_text (type), lowest);
+ *last = lowest;
+ return TRUE;
+}
+
+/**
+ * dkp_qos_request_latency:
+ *
+ * Return value: a new random cookie
+ **/
+void
+dkp_qos_request_latency (DkpQos *qos, const gchar *type_text, gint value, gboolean persistent, DBusGMethodInvocation *context)
+{
+ DkpQosRequestObj *obj;
+ gchar *sender = NULL;
+ const gchar *auth;
+ GError *error;
+ guint uid;
+ gint pid;
+ PolKitCaller *caller = NULL;
+ polkit_bool_t retval;
+ DkpQosType type;
+
+ /* get correct data */
+ type = dkp_qos_type_from_text (type_text);
+ if (type == DKP_QOS_TYPE_UNKNOWN) {
+ error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "type invalid: %s", type_text);
+ dbus_g_method_return_error (context, error);
+ goto out;
+ }
+
+ /* as we are async, we can get the sender */
+ sender = dbus_g_method_get_sender (context);
+ if (sender == NULL) {
+ error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "no DBUS sender");
+ dbus_g_method_return_error (context, error);
+ goto out;
+ }
+
+ /* get the caller */
+ caller = dkp_polkit_get_caller (qos->priv->polkit, context);
+ if (caller == NULL)
+ goto out;
+
+ /* check auth */
+ if (persistent)
+ auth = "org.freedesktop.devicekit.power.latency.request-latency-persistent";
+ else
+ auth = "org.freedesktop.devicekit.power.latency.request-latency";
+ if (!dkp_polkit_check_auth (qos->priv->polkit, caller, auth, context))
+ goto out;
+
+ /* get uid */
+ retval = polkit_caller_get_uid (caller, &uid);
+ if (!retval) {
+ error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "cannot get UID");
+ dbus_g_method_return_error (context, error);
+ goto out;
+ }
+
+ /* get pid */
+ retval = polkit_caller_get_pid (caller, &pid);
+ if (!retval) {
+ error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "cannot get PID");
+ dbus_g_method_return_error (context, error);
+ goto out;
+ }
+
+ /* seems okay, add to list */
+ obj = g_new (DkpQosRequestObj, 1);
+ obj->cookie = dkp_qos_generate_cookie (qos);
+ obj->sender = g_strdup (sender);
+ obj->value = value;
+ obj->uid = uid;
+ obj->uid = pid;
+ obj->persistent = persistent;
+ obj->type = type;
+ g_ptr_array_add (qos->priv->data, obj);
+
+ egg_debug ("Recieved Qos from '%s' (%i:%i)' saving as #%i",
+ obj->sender, obj->value, obj->persistent, obj->cookie);
+
+ /* TODO: if persistent add to datadase */
+
+ /* only emit event on the first one */
+ dkp_qos_latency_perhaps_changed (qos, type);
+ dbus_g_method_return (context, obj->cookie);
+out:
+ if (caller != NULL)
+ polkit_caller_unref (caller);
+ g_free (sender);
+}
+
+/**
+ * dkp_qos_free_data_obj:
+ **/
+static void
+dkp_qos_free_data_obj (DkpQosRequestObj *obj)
+{
+ g_free (obj->sender);
+ g_free (obj);
+}
+
+/**
+ * dkp_qos_cancel_request:
+ *
+ * Removes a cookie and associated data from the DkpQosRequestObj struct.
+ **/
+void
+dkp_qos_cancel_request (DkpQos *qos, guint cookie, DBusGMethodInvocation *context)
+{
+ DkpQosRequestObj *obj;
+ GError *error;
+ gchar *sender = NULL;
+ PolKitCaller *caller = NULL;
+
+ /* find the correct cookie */
+ obj = dkp_qos_find_from_cookie (qos, cookie);
+ if (obj == NULL) {
+ error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL,
+ "Cannot find request for #%i", cookie);
+ dbus_g_method_return_error (context, error);
+ goto out;
+ }
+
+ /* get the sender? */
+ sender = dbus_g_method_get_sender (context);
+ if (sender == NULL) {
+ error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "no DBUS sender");
+ dbus_g_method_return_error (context, error);
+ goto out;
+ }
+
+ /* are we not the sender? */
+ if (!egg_strequal (sender, obj->sender)) {
+ caller = dkp_polkit_get_caller (qos->priv->polkit, context);
+ if (caller == NULL)
+ goto out;
+ if (!dkp_polkit_check_auth (qos->priv->polkit, caller, "org.freedesktop.devicekit.power.latency.cancel-request", context))
+ goto out;
+ }
+
+ egg_debug ("Clear #%i", cookie);
+
+ /* remove object from list */
+ g_ptr_array_remove (qos->priv->data, obj);
+ dkp_qos_latency_perhaps_changed (qos, obj->type);
+
+ /* TODO: if persistent remove from datadase */
+
+ dkp_qos_free_data_obj (obj);
+
+ g_signal_emit (qos, signals [REQUESTS_CHANGED], 0);
+out:
+ if (caller != NULL)
+ polkit_caller_unref (caller);
+ g_free (sender);
+}
+
+/**
+ * dkp_qos_get_latency:
+ *
+ * Gets the current latency
+ **/
+gboolean
+dkp_qos_get_latency (DkpQos *qos, const gchar *type_text, gint *value, GError **error)
+{
+ DkpQosType type;
+
+ /* get correct data */
+ type = dkp_qos_type_from_text (type_text);
+ if (type == DKP_QOS_TYPE_UNKNOWN) {
+ *error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "type invalid: %s", type_text);
+ return FALSE;
+ }
+
+ /* get the lowest value for this type */
+ *value = dkp_qos_get_lowest (qos, type);
+ return TRUE;
+}
+
+/**
+ * dkp_qos_set_minimum_latency:
+ **/
+void
+dkp_qos_set_minimum_latency (DkpQos *qos, const gchar *type, gint value, DBusGMethodInvocation *context)
+{
+ egg_warning ("Not implimented");
+ return;
+}
+
+/**
+ * dkp_qos_get_latency_requests:
+ **/
+gboolean
+dkp_qos_get_latency_requests (DkpQos *qos, GPtrArray **requests, GError **error)
+{
+ egg_warning ("Not implimented");
+ return FALSE;
+}
+
+
+/**
+ * dkp_qos_remove_dbus:
+ **/
+static void
+dkp_qos_remove_dbus (DkpQos *qos, const gchar *sender)
+{
+ guint i;
+ GPtrArray *data;
+ DkpQosRequestObj *obj;
+
+ /* remove *any* senders that match the sender */
+ data = qos->priv->data;
+ for (i=0; i<data->len; i++) {
+ obj = g_ptr_array_index (data, i);
+ if (strcmp (obj->sender, sender) == 0) {
+ egg_debug ("Auto-revoked idle qos on %s", sender);
+ g_ptr_array_remove (qos->priv->data, obj);
+ dkp_qos_latency_perhaps_changed (qos, obj->type);
+ dkp_qos_free_data_obj (obj);
+ }
+ }
+}
+
+/**
+ * dkp_qos_name_owner_changed_cb:
+ **/
+static void
+dkp_qos_name_owner_changed_cb (DBusGProxy *proxy, const gchar *name, const gchar *prev, const gchar *new, DkpQos *qos)
+{
+ if (strlen (new) == 0)
+ dkp_qos_remove_dbus (qos, name);
+}
+
+/**
+ * dkp_qos_class_init:
+ **/
+static void
+dkp_qos_class_init (DkpQosClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = dkp_qos_finalize;
+
+ signals [LATENCY_CHANGED] =
+ g_signal_new ("latency-changed",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (DkpQosClass, latency_changed),
+ NULL, NULL, dkp_marshal_VOID__STRING_INT,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+ signals [REQUESTS_CHANGED] =
+ g_signal_new ("requests-changed",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (DkpQosClass, requests_changed),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ g_type_class_add_private (klass, sizeof (DkpQosPrivate));
+}
+
+/**
+ * dkp_qos_init:
+ **/
+static void
+dkp_qos_init (DkpQos *qos)
+{
+ guint i;
+ GError *error = NULL;
+
+ qos->priv = DKP_QOS_GET_PRIVATE (qos);
+ qos->priv->polkit = dkp_polkit_new ();
+ qos->priv->data = g_ptr_array_new ();
+
+ /* TODO: need to load persistent values */
+
+ /* setup lowest */
+ for (i=0; i<DKP_QOS_TYPE_UNKNOWN; i++)
+ qos->priv->last[i] = dkp_qos_get_lowest (qos, i);
+
+ qos->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (error != NULL) {
+ egg_warning ("Cannot connect to bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* register on the bus */
+ dbus_g_connection_register_g_object (qos->priv->connection, "/org/freedesktop/DeviceKit/Power", G_OBJECT (qos));
+
+ /* watch NOC */
+ qos->priv->proxy = dbus_g_proxy_new_for_name_owner (qos->priv->connection, DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, NULL);
+ dbus_g_proxy_add_signal (qos->priv->proxy, "NameOwnerChanged",
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (qos->priv->proxy, "NameOwnerChanged",
+ G_CALLBACK (dkp_qos_name_owner_changed_cb), qos, NULL);
+}
+
+/**
+ * dkp_qos_finalize:
+ **/
+static void
+dkp_qos_finalize (GObject *object)
+{
+ DkpQos *qos;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (DKP_IS_QOS (object));
+
+ qos = DKP_QOS (object);
+ qos->priv = DKP_QOS_GET_PRIVATE (qos);
+
+ g_ptr_array_foreach (qos->priv->data, (GFunc) dkp_qos_free_data_obj, NULL);
+ g_ptr_array_free (qos->priv->data, TRUE);
+ g_object_unref (qos->priv->proxy);
+ g_object_unref (qos->priv->polkit);
+
+ G_OBJECT_CLASS (dkp_qos_parent_class)->finalize (object);
+}
+
+/**
+ * dkp_qos_new:
+ **/
+DkpQos *
+dkp_qos_new (void)
+{
+ DkpQos *qos;
+ qos = g_object_new (DKP_TYPE_QOS, NULL);
+ return DKP_QOS (qos);
+}
+
diff --git a/src/dkp-qos.h b/src/dkp-qos.h
new file mode 100644
index 0000000..ae002a9
--- /dev/null
+++ b/src/dkp-qos.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __DKP_QOS_H
+#define __DKP_QOS_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+#define DKP_TYPE_QOS (dkp_qos_get_type ())
+#define DKP_QOS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), DKP_TYPE_QOS, DkpQos))
+#define DKP_QOS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), DKP_TYPE_QOS, DkpQosClass))
+#define DKP_IS_QOS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), DKP_TYPE_QOS))
+#define DKP_IS_QOS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), DKP_TYPE_QOS))
+#define DKP_QOS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DKP_TYPE_QOS, DkpQosClass))
+
+typedef struct DkpQosPrivate DkpQosPrivate;
+
+typedef struct
+{
+ GObject parent;
+ DkpQosPrivate *priv;
+} DkpQos;
+
+typedef struct
+{
+ GObjectClass parent_class;
+ void (* latency_changed) (DkpQos *qos,
+ const gchar *type,
+ gint value);
+ void (* requests_changed) (DkpQos *qos);
+} DkpQosClass;
+
+DkpQos *dkp_qos_new (void);
+GType dkp_qos_get_type (void);
+void dkp_qos_request_latency (DkpQos *qos,
+ const gchar *type,
+ gint value,
+ gboolean persistent,
+ DBusGMethodInvocation *context);
+void dkp_qos_cancel_request (DkpQos *qos,
+ guint32 cookie,
+ DBusGMethodInvocation *context);
+void dkp_qos_set_minimum_latency (DkpQos *qos,
+ const gchar *type,
+ gint value,
+ DBusGMethodInvocation *context);
+gboolean dkp_qos_get_latency (DkpQos *qos,
+ const gchar *type,
+ gint *value,
+ GError **error);
+gboolean dkp_qos_get_latency_requests (DkpQos *qos,
+ GPtrArray **requests,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __DKP_QOS_H */