summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2012-04-24 16:59:01 +0200
committerAleksander Morgado <aleksander@lanedo.com>2012-07-03 16:08:48 +0200
commit3acf5ce303eb101ce937e3bae2622faace3eefd9 (patch)
treefb99d25642cae8d461645f27eebb3c85a1ed38f1
parentd3d9360d4998a1d5ba96e5f8d93229afd7941915 (diff)
device: allow clients to get registered if they want to receive indication messages
-rw-r--r--src/qmi-client.c35
-rw-r--r--src/qmi-client.h8
-rw-r--r--src/qmi-device.c232
-rw-r--r--src/qmi-device.h9
4 files changed, 239 insertions, 45 deletions
diff --git a/src/qmi-client.c b/src/qmi-client.c
index 45570ba..e504d83 100644
--- a/src/qmi-client.c
+++ b/src/qmi-client.c
@@ -219,6 +219,7 @@ qmi_client_release (QmiClient *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ GError *error = NULL;
GSimpleAsyncResult *result;
result = g_simple_async_result_new (G_OBJECT (self),
@@ -226,6 +227,14 @@ qmi_client_release (QmiClient *self,
user_data,
qmi_client_release);
+ /* Unregister client from the device */
+ if (!qmi_device_unregister_client (self->priv->device, self, &error)) {
+ g_simple_async_result_take_error (result, error);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ return;
+ }
+
/* The CTL service client uses the implicit client (CID == 0),
* no need to explicitly release it */
if (self->priv->service == QMI_SERVICE_CTL) {
@@ -251,6 +260,15 @@ qmi_client_release (QmiClient *self,
(GAsyncReadyCallback)client_ctl_release_cid_ready,
result);
}
+/*****************************************************************************/
+
+void
+qmi_client_process_indication (QmiClient *self,
+ QmiMessage *message)
+{
+ if (QMI_CLIENT_GET_CLASS (self)->process_indication)
+ QMI_CLIENT_GET_CLASS (self)->process_indication (self, message);
+}
/*****************************************************************************/
/* Init */
@@ -287,6 +305,13 @@ allocate_cid_ready (QmiClientCtl *client_ctl,
/* Store the CID */
self->priv->cid = cid;
+ /* Register the client in the device in order to get notifications */
+ if (!qmi_device_register_client (self->priv->device, self, &error)) {
+ g_prefix_error (&error, "Couldn't register client in device: ");
+ g_simple_async_result_take_error (simple, error);
+ } else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+
g_simple_async_result_complete (simple);
g_object_unref (simple);
g_object_unref (self);
@@ -327,7 +352,14 @@ initable_init_async (GAsyncInitable *initable,
}
/* For the QmiClientCtl, just done we are */
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
+
+ /* Register the client in the device in order to get notifications */
+ if (!qmi_device_register_client (self->priv->device, self, &error)) {
+ g_prefix_error (&error, "Couldn't register client in device: ");
+ g_simple_async_result_take_error (result, error);
+ } else
+ g_simple_async_result_set_op_res_gboolean (result, TRUE);
+
g_simple_async_result_complete_in_idle (result);
g_object_unref (result);
}
@@ -387,7 +419,6 @@ client_release_cid_on_dispose (QmiClient *self)
ctx);
}
-
/*****************************************************************************/
static void
diff --git a/src/qmi-client.h b/src/qmi-client.h
index 637bed7..b89e3e3 100644
--- a/src/qmi-client.h
+++ b/src/qmi-client.h
@@ -52,6 +52,10 @@ struct _QmiClient {
struct _QmiClientClass {
GObjectClass parent;
+
+ /* Virtual method to get indications processed */
+ void (* process_indication) (QmiClient *self,
+ QmiMessage *message);
};
GType qmi_client_get_type (void);
@@ -71,6 +75,10 @@ gboolean qmi_client_release_finish (QmiClient *self,
GAsyncResult *res,
GError **error);
+/* not part of the public API */
+void qmi_client_process_indication (QmiClient *self,
+ QmiMessage *message);
+
G_END_DECLS
#endif /* _LIBQMI_GLIB_QMI_CLIENT_H_ */
diff --git a/src/qmi-device.c b/src/qmi-device.c
index 5d103e3..09185f5 100644
--- a/src/qmi-device.c
+++ b/src/qmi-device.c
@@ -59,6 +59,9 @@ struct _QmiDevicePrivate {
/* HT to keep track of ongoing transactions */
GHashTable *transactions;
+
+ /* HT of clients that want to get indications */
+ GHashTable *registered_clients;
};
#define BUFFER_SIZE 2048
@@ -352,8 +355,181 @@ qmi_device_is_open (QmiDevice *self)
}
/*****************************************************************************/
+/* Register clients that want to receive indications */
+
+static gpointer
+build_registered_client_key (guint8 cid,
+ QmiService service)
+{
+ return GUINT_TO_POINTER (((guint8)service << 8) | cid);
+}
+
+gboolean
+qmi_device_register_client (QmiDevice *self,
+ QmiClient *client,
+ GError **error)
+{
+ gpointer key;
+
+ if (G_UNLIKELY (!self->priv->registered_clients))
+ self->priv->registered_clients = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ g_object_unref);
+
+ key = build_registered_client_key (qmi_client_get_cid (client),
+ qmi_client_get_service (client));
+ /* Only add the new client if not already registered one with the same CID
+ * for the same service */
+ if (g_hash_table_lookup (self->priv->registered_clients, key)) {
+ g_set_error (error,
+ QMI_CORE_ERROR,
+ QMI_CORE_ERROR_FAILED,
+ "A client with CID '%u' and service '%s' is already registered",
+ qmi_client_get_cid (client),
+ qmi_service_get_string (qmi_client_get_service (client)));
+ return FALSE;
+ }
+
+ g_hash_table_insert (self->priv->registered_clients,
+ key,
+ g_object_ref (client));
+ return TRUE;
+}
+
+gboolean
+qmi_device_unregister_client (QmiDevice *self,
+ QmiClient *client,
+ GError **error)
+{
+ gpointer key;
+
+ key = build_registered_client_key (qmi_client_get_cid (client),
+ qmi_client_get_service (client));
+ if (!g_hash_table_remove (self->priv->registered_clients, key)) {
+ g_set_error (error,
+ QMI_CORE_ERROR,
+ QMI_CORE_ERROR_FAILED,
+ "Cannot unregister client with CID '%u' and service '%s': Not found",
+ qmi_client_get_cid (client),
+ qmi_service_get_string (qmi_client_get_service (client)));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
/* Open device */
+typedef struct {
+ QmiClient *client;
+ QmiMessage *message;
+} IdleIndicationContext;
+
+static gboolean
+process_indication_idle (IdleIndicationContext *ctx)
+{
+ g_assert (ctx->client != NULL);
+ g_assert (ctx->message != NULL);
+
+ qmi_client_process_indication (ctx->client, ctx->message);
+
+ g_object_unref (ctx->client);
+ qmi_message_unref (ctx->message);
+ g_slice_free (IdleIndicationContext, ctx);
+ return FALSE;
+}
+
+static void
+report_indication (QmiClient *client,
+ QmiMessage *message)
+{
+ IdleIndicationContext *ctx;
+
+ /* Setup an idle to Pass the indication down to the client */
+ ctx = g_slice_new (IdleIndicationContext);
+ ctx->client = g_object_ref (client);
+ ctx->message = qmi_message_ref (message);
+ g_idle_add ((GSourceFunc)process_indication_idle, ctx);
+}
+
+static void
+process_message (QmiDevice *self,
+ QmiMessage *message)
+{
+ GError *error = NULL;
+
+ /* Ensure the read message is valid */
+ if (!qmi_message_check (message, &error)) {
+ g_warning ("Invalid QMI message received: %s",
+ error->message);
+ g_error_free (error);
+ return;
+ }
+
+#ifdef MESSAGE_ENABLE_TRACE
+ {
+ gchar *printable;
+
+ printable = qmi_message_get_printable (message, ">>>>>> ");
+ g_debug ("[%s] Received message...\n%s",
+ self->priv->path_display,
+ printable);
+ g_free (printable);
+ }
+#endif /* MESSAGE_ENABLE_TRACE */
+
+ if (qmi_message_is_indication (message)) {
+ if (qmi_message_get_client_id (message) == QMI_CID_BROADCAST) {
+ GHashTableIter iter;
+ gpointer key;
+ QmiClient *client;
+
+ g_hash_table_iter_init (&iter, self->priv->registered_clients);
+ while (g_hash_table_iter_next (&iter, &key, (gpointer *)&client)) {
+ /* For broadcast messages, report them just if the service matches */
+ if (qmi_message_get_service (message) == qmi_client_get_service (client))
+ report_indication (client, message);
+ }
+ } else {
+ QmiClient *client;
+
+ client = g_hash_table_lookup (self->priv->registered_clients,
+ build_registered_client_key (qmi_message_get_client_id (message),
+ qmi_message_get_service (message)));
+ if (client)
+ report_indication (client, message);
+ }
+
+ return;
+ }
+
+ if (qmi_message_is_response (message)) {
+ Transaction *tr;
+
+ tr = device_match_transaction (self, message);
+ if (!tr)
+ g_debug ("[%s] No transaction matched in received message",
+ self->priv->path_display);
+ else {
+ /* Received message is a response in a transaction; handle QMI protocol
+ * errors */
+ if (!qmi_message_get_response_result (message, &error)) {
+ transaction_complete_and_free (tr, NULL, error);
+ g_error_free (error);
+ } else {
+ /* Report the reply message */
+ transaction_complete_and_free (tr, message, NULL);
+ }
+ }
+ return;
+ }
+
+ g_debug ("[%s] Message received but it is neither an indication nor a response. Skipping it.",
+ self->priv->path_display);
+}
+
static void
parse_response (QmiDevice *self)
{
@@ -383,47 +559,8 @@ parse_response (QmiDevice *self)
0,
qmi_message_get_length (message));
- /* Ensure the read message is valid */
- if (!qmi_message_check (message, &error)) {
- g_warning ("Invalid QMI message received: %s",
- error->message);
- g_error_free (error);
- } else if (qmi_message_get_client_id (message) == QMI_CID_BROADCAST) {
- g_debug ("Broadcast QMI message received");
- /* TODO: notify to clients */
- } else {
- Transaction *tr;
-
- /* Got a valid QMI message, do whatever we need to do with it now */
-
-#ifdef MESSAGE_ENABLE_TRACE
- {
- gchar *printable;
-
- printable = qmi_message_get_printable (message, ">>>>>> ");
- g_debug ("[%s] Received message...\n%s",
- self->priv->path_display,
- printable);
- g_free (printable);
- }
-#endif /* MESSAGE_ENABLE_TRACE */
-
- tr = device_match_transaction (self, message);
- if (!tr)
- g_debug ("[%s] No transaction matched in received message",
- self->priv->path_display);
- else {
- /* Received message is a response in a transaction; handle QMI protocol
- * errors */
- if (!qmi_message_get_response_result (message, &error)) {
- transaction_complete_and_free (tr, NULL, error);
- g_error_free (error);
- } else {
- /* Report the reply message */
- transaction_complete_and_free (tr, message, NULL);
- }
- }
- }
+ /* Play with the received message */
+ process_message (self, message);
qmi_message_unref (message);
} while (self->priv->response->len > 0);
@@ -1064,8 +1201,17 @@ finalize (GObject *object)
/* Transactions keep refs to the device, so it's actually
* impossible to have any content in the HT */
- g_assert (g_hash_table_size (self->priv->transactions) == 0);
- g_hash_table_unref (self->priv->transactions);
+ if (self->priv->transactions) {
+ g_assert (g_hash_table_size (self->priv->transactions) == 0);
+ g_hash_table_unref (self->priv->transactions);
+ }
+
+ /* Registered clients keep refs to the device, so it's actually
+ * impossible to have any content in the HT */
+ if (self->priv->registered_clients) {
+ g_assert (g_hash_table_size (self->priv->registered_clients) == 0);
+ g_hash_table_unref (self->priv->registered_clients);
+ }
g_free (self->priv->path);
g_free (self->priv->path_display);
diff --git a/src/qmi-device.h b/src/qmi-device.h
index 8d20489..5289cda 100644
--- a/src/qmi-device.h
+++ b/src/qmi-device.h
@@ -31,6 +31,7 @@ G_BEGIN_DECLS
* as it is not installable */
typedef struct _QmiMessage QmiMessage;
+typedef struct _QmiClient QmiClient;
typedef struct _QmiClientCtl QmiClientCtl;
#define QMI_TYPE_DEVICE (qmi_device_get_type ())
@@ -95,6 +96,14 @@ QmiMessage *qmi_device_command_finish (QmiDevice *self,
GAsyncResult *res,
GError **error);
+/* not part of the public API */
+gboolean qmi_device_register_client (QmiDevice *self,
+ QmiClient *client,
+ GError **error);
+gboolean qmi_device_unregister_client (QmiDevice *self,
+ QmiClient *client,
+ GError **error);
+
G_END_DECLS
#endif /* _LIBQMI_GLIB_QMI_DEVICE_H_ */