diff --git a/cli/qmicli.c b/cli/qmicli.c index 6afc8fa..eddd755 100644 --- a/cli/qmicli.c +++ b/cli/qmicli.c @@ -200,6 +200,7 @@ device_new_ready (GObject *unused, /* Open the device */ qmi_device_open (ctx->device, + 5, ctx->cancellable, (GAsyncReadyCallback)device_open_ready, ctx); diff --git a/src/qmi-device.c b/src/qmi-device.c index 70b73df..b321de7 100644 --- a/src/qmi-device.c +++ b/src/qmi-device.c @@ -67,6 +67,7 @@ struct _QmiDevicePrivate { typedef struct { QmiMessage *message; GSimpleAsyncResult *result; + guint timeout_id; } Transaction; static Transaction * @@ -77,7 +78,7 @@ transaction_new (QmiDevice *self, { Transaction *tr; - tr = g_slice_new (Transaction); + tr = g_slice_new0 (Transaction); tr->message = qmi_message_ref (message); tr->result = g_simple_async_result_new (G_OBJECT (self), callback, @@ -94,6 +95,9 @@ transaction_complete_and_free (Transaction *tr, { g_assert (reply != NULL || error != NULL); + if (tr->timeout_id) + g_source_remove (tr->timeout_id); + if (reply) g_simple_async_result_set_op_res_gpointer (tr->result, qmi_message_ref (reply), @@ -146,34 +150,76 @@ build_transaction_key (QmiMessage *message) return key; } +static Transaction * +device_release_transaction (QmiDevice *self, + gpointer key) +{ + Transaction *tr = NULL; + + if (self->priv->transactions) { + tr = g_hash_table_lookup (self->priv->transactions, key); + if (tr) + /* If found, remove it from the HT */ + g_hash_table_remove (self->priv->transactions, key); + } + + return tr; +} + +typedef struct { + QmiDevice *self; + gpointer key; +} TransactionTimeoutContext; + +static gboolean +transaction_timed_out (TransactionTimeoutContext *ctx) +{ + Transaction *tr; + GError *error = NULL; + + tr = device_release_transaction (ctx->self, ctx->key); + tr->timeout_id = 0; + + /* Complete transaction with a timeout error */ + error = g_error_new (QMI_CORE_ERROR, + QMI_CORE_ERROR_TIMEOUT, + "Transaction timed out"); + transaction_complete_and_free (tr, NULL, error); + g_error_free (error); + + return FALSE; +} + static gboolean device_store_transaction (QmiDevice *self, - Transaction *tr) + Transaction *tr, + guint timeout) { + TransactionTimeoutContext *timeout_ctx; + gpointer key; + if (G_UNLIKELY (!self->priv->transactions)) self->priv->transactions = g_hash_table_new (g_direct_hash, g_direct_equal); - g_hash_table_insert (self->priv->transactions, - build_transaction_key (tr->message), - tr); + key = build_transaction_key (tr->message); + g_hash_table_insert (self->priv->transactions, key, tr); + + /* Once it gets into the HT, setup the timeout */ + timeout_ctx = g_slice_new (TransactionTimeoutContext); + timeout_ctx->self = self; + timeout_ctx->key = key; + tr->timeout_id = g_timeout_add_seconds (timeout, + (GSourceFunc)transaction_timed_out, + timeout_ctx); } static Transaction * device_match_transaction (QmiDevice *self, QmiMessage *message) { - Transaction *tr; - gpointer key; - /* msg can be either the original message or the response */ - key = build_transaction_key (message); - tr = g_hash_table_lookup (self->priv->transactions, key); - if (tr) - /* If found, remove it from the HT */ - g_hash_table_remove (self->priv->transactions, key); - - return tr; + return device_release_transaction (self, build_transaction_key (message)); } /*****************************************************************************/ @@ -529,6 +575,7 @@ version_info_ready (QmiDevice *self, /** * qmi_device_open: * @self: a #QmiDevice. + * @timeout: maximum time, in seconds, to wait for the device to be opened. * * Asynchronously opens a #QmiDevice for I/O. * @@ -537,6 +584,7 @@ version_info_ready (QmiDevice *self, */ void qmi_device_open (QmiDevice *self, + guint timeout, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) @@ -566,6 +614,7 @@ qmi_device_open (QmiDevice *self, version_info_request = qmi_message_ctl_version_info_new (device_get_ctl_transaction_id (self)); qmi_device_command (self, version_info_request, + timeout, cancellable, (GAsyncReadyCallback)version_info_ready, result); @@ -647,6 +696,7 @@ qmi_device_command_finish (QmiDevice *self, void qmi_device_command (QmiDevice *self, QmiMessage *message, + guint timeout, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) @@ -694,7 +744,7 @@ qmi_device_command (QmiDevice *self, } /* Setup context to match response */ - device_store_transaction (self, tr); + device_store_transaction (self, tr, timeout); written = 0; if (g_io_channel_write_chars (self->priv->iochannel, diff --git a/src/qmi-device.h b/src/qmi-device.h index 48c7969..d89f443 100644 --- a/src/qmi-device.h +++ b/src/qmi-device.h @@ -70,6 +70,7 @@ const gchar *qmi_device_get_path_display (QmiDevice *self); gboolean qmi_device_is_open (QmiDevice *self); void qmi_device_open (QmiDevice *self, + guint timeout, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); @@ -82,6 +83,7 @@ gboolean qmi_device_close (QmiDevice *self, void qmi_device_command (QmiDevice *self, QmiMessage *message, + guint timeout, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); diff --git a/src/qmi-errors.h b/src/qmi-errors.h index 8d546c8..9f87041 100644 --- a/src/qmi-errors.h +++ b/src/qmi-errors.h @@ -27,6 +27,7 @@ * QmiCoreError: * @QMI_CORE_ERROR_FAILED: Operation failed. * @QMI_CORE_ERROR_WRONG_STATE: Operation cannot be executed in the current state. + * @QMI_CORE_ERROR_TIMEOUT: Operation timed out. * @QMI_CORE_ERROR_INVALID_ARGS: Invalid arguments given. * @QMI_CORE_ERROR_INVALID_MESSAGE: QMI message is invalid. * @QMI_CORE_ERROR_TLV_NOT_FOUND: TLV not found. @@ -37,6 +38,7 @@ typedef enum { QMI_CORE_ERROR_FAILED, QMI_CORE_ERROR_WRONG_STATE, + QMI_CORE_ERROR_TIMEOUT, QMI_CORE_ERROR_INVALID_ARGS, QMI_CORE_ERROR_INVALID_MESSAGE, QMI_CORE_ERROR_TLV_NOT_FOUND,