From 13b53c6a1d196101779de19af0e4000c87489030 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 10 Oct 2012 19:49:21 +0200 Subject: libqmi-glib,qmi-codegen: timed out operations will issue an ABORT message Messages can now be tagged with a special 'abort' keyword, so that whenever the message times out we issue a new ABORT command to cancel the specific timed out request. This support is currently only available for the NAS and WDS services, which are the ones supporting ABORT for their long-running operations. --- build-aux/qmi-codegen/Client.py | 112 +++++++++++++++++++++++++++++++++---- build-aux/qmi-codegen/Container.py | 47 +++++++++------- build-aux/qmi-codegen/Field.py | 18 +++--- build-aux/qmi-codegen/Message.py | 10 +++- data/qmi-service-nas.json | 15 +++++ data/qmi-service-wds.json | 15 +++++ libqmi-glib/generated/Makefile.am | 3 +- 7 files changed, 179 insertions(+), 41 deletions(-) diff --git a/build-aux/qmi-codegen/Client.py b/build-aux/qmi-codegen/Client.py index 5aa9478..06b5aa7 100644 --- a/build-aux/qmi-codegen/Client.py +++ b/build-aux/qmi-codegen/Client.py @@ -261,14 +261,20 @@ class Client: Emits the async methods for each known request/response """ def __emit_methods(self, hfile, cfile, message_list): - translations = { 'underscore' : utils.build_underscore_name(self.name), - 'camelcase' : utils.build_camelcase_name (self.name) } + translations = { 'underscore' : utils.build_underscore_name(self.name), + 'camelcase' : utils.build_camelcase_name (self.name), + 'service_lowercase' : string.lower(self.service), + 'service_uppercase' : string.upper(self.service), + 'service_camelcase' : string.capwords(self.service) } for message in message_list.list: if message.type == 'Indication': continue + if message.static: + continue + translations['message_name'] = message.name translations['message_underscore'] = utils.build_underscore_name(message.name) translations['message_fullname_underscore'] = utils.build_underscore_name(message.fullname) @@ -306,11 +312,11 @@ class Client: ' * ${underscore}_${message_underscore}_finish:\n' ' * @self: a #${camelcase}.\n' ' * @res: the #GAsyncResult obtained from the #GAsyncReadyCallback passed to ${underscore}_${message_underscore}().\n' - ' * @error: Return location for error or %%NULL.\n' + ' * @error: Return location for error or %NULL.\n' ' *\n' ' * Finishes an async operation started with ${underscore}_${message_underscore}().\n' ' *\n' - ' * Returns: a #${output_camelcase}, or %%NULL if @error is set. The returned value should be freed with ${output_underscore}_unref().\n' + ' * Returns: a #${output_camelcase}, or %NULL if @error is set. The returned value should be freed with ${output_underscore}_unref().\n' ' */\n' '${output_camelcase} *\n' '${underscore}_${message_underscore}_finish (\n' @@ -322,7 +328,35 @@ class Client: ' return NULL;\n' '\n' ' return ${output_underscore}_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));\n' - '}\n' + '}\n') + + if message.abort: + template += ( + '\n' + 'static void\n' + '${message_underscore}_abort_ready (\n' + ' QmiDevice *device,\n' + ' GAsyncResult *res)\n' + '{\n' + ' GError *error = NULL;\n' + ' QmiMessage *reply;\n' + ' QmiMessage${service_camelcase}AbortOutput *output;\n' + '\n' + ' reply = qmi_device_command_finish (device, res, &error);\n' + ' if (reply) {\n' + ' output = __qmi_message_${service_lowercase}_abort_response_parse (reply, &error);\n' + ' if (output)\n' + ' qmi_message_${service_lowercase}_abort_output_unref (output);\n' + ' qmi_message_unref (reply);\n' + ' }\n' + '\n' + ' if (error) {\n' + ' g_debug ("Operation to abort \'${message_name}\' failed: %s", error->message);\n' + ' g_error_free (error);\n' + ' }\n' + '}\n') + + template += ( '\n' 'static void\n' '${message_underscore}_ready (\n' @@ -335,7 +369,47 @@ class Client: ' ${output_camelcase} *output;\n' '\n' ' reply = qmi_device_command_finish (device, res, &error);\n' - ' if (!reply) {\n' + ' if (!reply) {\n') + + if message.abort: + template += ( + ' if (g_error_matches (error, QMI_CORE_ERROR, QMI_CORE_ERROR_TIMEOUT)) {\n' + ' QmiMessage *abort;\n' + ' GObject *self;\n' + ' guint16 transaction_id;\n' + ' QmiMessage${service_camelcase}AbortInput *input;\n' + '\n' + ' self = g_async_result_get_source_object (G_ASYNC_RESULT (simple));\n' + ' g_assert (self != NULL);\n' + '\n' + ' transaction_id = (guint16) GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (simple),\n' + ' "transaction-id"));\n' + ' g_assert (transaction_id != 0);\n' + '\n' + ' input = qmi_message_${service_lowercase}_abort_input_new ();\n' + ' qmi_message_${service_lowercase}_abort_input_set_transaction_id (\n' + ' input,\n' + ' transaction_id,\n' + ' NULL);\n' + ' abort = __qmi_message_${service_lowercase}_abort_request_create (\n' + ' qmi_client_get_next_transaction_id (QMI_CLIENT (self)),\n' + ' qmi_client_get_cid (QMI_CLIENT (self)),\n' + ' input,\n' + ' NULL);\n' + ' g_assert (abort != NULL);\n' + ' qmi_device_command (device,\n' + ' abort,\n' + ' 30,\n' + ' NULL,\n' + ' (GAsyncReadyCallback)${message_underscore}_abort_ready,\n' + ' NULL);\n' + ' qmi_message_${service_lowercase}_abort_input_unref (input);\n' + ' qmi_message_unref (abort);\n' + ' g_object_unref (self);\n' + ' }\n' + '\n') + + template += ( ' g_simple_async_result_take_error (simple, error);\n' ' g_simple_async_result_complete (simple);\n' ' g_object_unref (simple);\n' @@ -360,7 +434,7 @@ class Client: ' * @self: a #${camelcase}.\n' ' * @${input_doc}\n' ' * @timeout: maximum time to wait for the method to complete, in seconds.\n' - ' * @cancellable: a #GCancellable or %%NULL.\n' + ' * @cancellable: a #GCancellable or %NULL.\n' ' * @callback: a #GAsyncReadyCallback to call when the request is satisfied.\n' ' * @user_data: user data to pass to @callback.\n' ' *\n' @@ -372,8 +446,10 @@ class Client: ' */\n' 'void\n' '${underscore}_${message_underscore} (\n' - ' ${camelcase} *self,\n' - ' %s,\n' + ' ${camelcase} *self,\n') + template += ( + ' %s,\n' % input_arg_template) + template += ( ' guint timeout,\n' ' GCancellable *cancellable,\n' ' GAsyncReadyCallback callback,\n' @@ -382,14 +458,17 @@ class Client: ' GSimpleAsyncResult *result;\n' ' QmiMessage *request;\n' ' GError *error = NULL;\n' + ' guint16 transaction_id;\n' '\n' ' result = g_simple_async_result_new (G_OBJECT (self),\n' ' callback,\n' ' user_data,\n' ' ${underscore}_${message_underscore});\n' '\n' + ' transaction_id = qmi_client_get_next_transaction_id (QMI_CLIENT (self));\n' + '\n' ' request = __${message_fullname_underscore}_request_create (\n' - ' qmi_client_get_next_transaction_id (QMI_CLIENT (self)),\n' + ' transaction_id,\n' ' qmi_client_get_cid (QMI_CLIENT (self)),\n' ' ${input_var},\n' ' &error);\n' @@ -399,7 +478,16 @@ class Client: ' g_simple_async_result_complete_in_idle (result);\n' ' g_object_unref (result);\n' ' return;\n' - ' }\n' + ' }\n') + + if message.abort: + template += ( + '\n' + ' g_object_set_data (G_OBJECT (result),\n' + ' "transaction-id",\n' + ' GUINT_TO_POINTER (transaction_id));\n') + + template += ( '\n' ' qmi_device_command (QMI_DEVICE (qmi_client_peek_device (QMI_CLIENT (self))),\n' ' request,\n' @@ -409,7 +497,7 @@ class Client: ' result);\n' ' qmi_message_unref (request);\n' '}\n' - '\n' % input_arg_template) + '\n') cfile.write(string.Template(template).substitute(translations)) diff --git a/build-aux/qmi-codegen/Container.py b/build-aux/qmi-codegen/Container.py index f15e6b1..d7488f1 100644 --- a/build-aux/qmi-codegen/Container.py +++ b/build-aux/qmi-codegen/Container.py @@ -33,7 +33,7 @@ class Container: """ Constructor """ - def __init__(self, prefix, container_type, dictionary, common_objects_dictionary): + def __init__(self, prefix, container_type, dictionary, common_objects_dictionary, static): # The field container prefix usually contains the name of the Message, # e.g. "Qmi Message Ctl Something" self.prefix = prefix @@ -48,6 +48,8 @@ class Container: self.name = container_type + self.static = static + # Create the composed full name (prefix + name), # e.g. "Qmi Message Ctl Something Output" self.fullname = self.prefix + ' ' + self.name @@ -88,9 +90,9 @@ class Container: if field_dictionary['type'] == 'TLV': if field_dictionary['format'] == 'struct' and \ field_dictionary['name'] == 'Result': - self.fields.append(FieldResult(self.fullname, field_dictionary, common_objects_dictionary, container_type)) + self.fields.append(FieldResult(self.fullname, field_dictionary, common_objects_dictionary, container_type, static)) else: - self.fields.append(Field(self.fullname, field_dictionary, common_objects_dictionary, container_type)) + self.fields.append(Field(self.fullname, field_dictionary, common_objects_dictionary, container_type, static)) """ @@ -124,7 +126,7 @@ class Container: ' * using the provided API.\n' ' */\n' 'typedef struct _${camelcase} ${camelcase};\n' - 'GType ${underscore}_get_type (void) G_GNUC_CONST;\n' + '${static}GType ${underscore}_get_type (void) G_GNUC_CONST;\n' '#define ${type_macro} (${underscore}_get_type ())\n') hfile.write(string.Template(template).substitute(translations)) @@ -159,17 +161,21 @@ class Container: # Emit container core header template = ( '\n' - '${camelcase} *${underscore}_ref (${camelcase} *self);\n' - 'void ${underscore}_unref (${camelcase} *self);\n') + '${static}${camelcase} *${underscore}_ref (${camelcase} *self);\n' + '${static}void ${underscore}_unref (${camelcase} *self);\n') if self.readonly == False: template += ( - '${camelcase} *${underscore}_new (void);\n') - hfile.write(string.Template(template).substitute(translations)) + '${static}${camelcase} *${underscore}_new (void);\n') + + if self.static: + cfile.write(string.Template(template).substitute(translations)) + else: + hfile.write(string.Template(template).substitute(translations)) # Emit container core source template = ( '\n' - 'GType\n' + '${static}GType\n' '${underscore}_get_type (void)\n' '{\n' ' static volatile gsize g_define_type_id__volatile = 0;\n' @@ -194,7 +200,7 @@ class Container: ' *\n' ' * Returns: the new reference to @self.\n' ' */\n' - '${camelcase} *\n' + '${static}${camelcase} *\n' '${underscore}_ref (${camelcase} *self)\n' '{\n' ' g_return_val_if_fail (self != NULL, NULL);\n' @@ -210,7 +216,7 @@ class Container: ' * Atomically decrements the reference count of @self by one.\n' ' * If the reference count drops to 0, @self is completely disposed.\n' ' */\n' - 'void\n' + '${static}void\n' '${underscore}_unref (${camelcase} *self)\n' '{\n' ' g_return_if_fail (self != NULL);\n' @@ -241,7 +247,7 @@ class Container: ' *\n' ' * Returns: the newly created #${camelcase}. The returned value should be freed with ${underscore}_unref().\n' ' */\n' - '${camelcase} *\n' + '${static}${camelcase} *\n' '${underscore}_new (void)\n' '{\n' ' ${camelcase} *self;\n' @@ -259,12 +265,15 @@ class Container: def emit(self, hfile, cfile): translations = { 'name' : self.name, 'camelcase' : utils.build_camelcase_name (self.fullname), - 'underscore' : utils.build_underscore_name (self.fullname) } + 'underscore' : utils.build_underscore_name (self.fullname), + 'static' : 'static ' if self.static else '' } + + auxfile = cfile if self.static else hfile if self.fields is None: template = ('\n' '/* Note: no fields in the ${name} container */\n') - hfile.write(string.Template(template).substitute(translations)) + auxfile.write(string.Template(template).substitute(translations)) cfile.write(string.Template(template).substitute(translations)) return @@ -272,8 +281,8 @@ class Container: # Emit field getter/setter if self.fields is not None: for field in self.fields: - field.emit_types(hfile, cfile) - self.__emit_types(hfile, cfile, translations) + field.emit_types(auxfile, cfile) + self.__emit_types(auxfile, cfile, translations) # Emit TLV enums self.__emit_tlv_ids_enum(cfile) @@ -281,12 +290,12 @@ class Container: # Emit fields if self.fields is not None: for field in self.fields: - field.emit_getter(hfile, cfile) + field.emit_getter(auxfile, cfile) if self.readonly == False: - field.emit_setter(hfile, cfile) + field.emit_setter(auxfile, cfile) # Emit the container core - self.__emit_core(hfile, cfile, translations) + self.__emit_core(auxfile, cfile, translations) """ diff --git a/build-aux/qmi-codegen/Field.py b/build-aux/qmi-codegen/Field.py index 1faff41..31005b3 100644 --- a/build-aux/qmi-codegen/Field.py +++ b/build-aux/qmi-codegen/Field.py @@ -32,7 +32,7 @@ class Field: """ Constructor """ - def __init__(self, prefix, dictionary, common_objects_dictionary, container_type): + def __init__(self, prefix, dictionary, common_objects_dictionary, container_type, static): # The field prefix, usually the name of the Container, # e.g. "Qmi Message Ctl Something Output" self.prefix = prefix @@ -46,6 +46,8 @@ class Field: self.type = dictionary['type'] # The container type, which must be either "Input" or "Output" self.container_type = container_type + # Whether the whole field is internally used only + self.static = static # Create the composed full name (prefix + name), # e.g. "Qmi Message Ctl Something Output Result" @@ -105,12 +107,13 @@ class Field: 'variable_getter_imp' : variable_getter_imp, 'underscore' : utils.build_underscore_name(self.name), 'prefix_camelcase' : utils.build_camelcase_name(self.prefix), - 'prefix_underscore' : utils.build_underscore_name(self.prefix) } + 'prefix_underscore' : utils.build_underscore_name(self.prefix), + 'static' : 'static ' if self.static else '' } # Emit the getter header template = ( '\n' - 'gboolean ${prefix_underscore}_get_${underscore} (\n' + '${static}gboolean ${prefix_underscore}_get_${underscore} (\n' ' ${prefix_camelcase} *self,\n' '${variable_getter_dec}' ' GError **error);\n') @@ -129,7 +132,7 @@ class Field: ' *\n' ' * Returns: %TRUE if the field is found, %FALSE otherwise.\n' ' */\n' - 'gboolean\n' + '${static}gboolean\n' '${prefix_underscore}_get_${underscore} (\n' ' ${prefix_camelcase} *self,\n' '${variable_getter_dec}' @@ -168,12 +171,13 @@ class Field: 'variable_setter_imp' : variable_setter_imp, 'underscore' : utils.build_underscore_name(self.name), 'prefix_camelcase' : utils.build_camelcase_name(self.prefix), - 'prefix_underscore' : utils.build_underscore_name(self.prefix) } + 'prefix_underscore' : utils.build_underscore_name(self.prefix), + 'static' : 'static ' if self.static else '' } # Emit the setter header template = ( '\n' - 'gboolean ${prefix_underscore}_set_${underscore} (\n' + '${static}gboolean ${prefix_underscore}_set_${underscore} (\n' ' ${prefix_camelcase} *self,\n' '${variable_setter_dec}' ' GError **error);\n') @@ -192,7 +196,7 @@ class Field: ' *\n' ' * Returns: %TRUE if @value was successfully set, %FALSE otherwise.\n' ' */\n' - 'gboolean\n' + '${static}gboolean\n' '${prefix_underscore}_set_${underscore} (\n' ' ${prefix_camelcase} *self,\n' '${variable_setter_dec}' diff --git a/build-aux/qmi-codegen/Message.py b/build-aux/qmi-codegen/Message.py index 57b33ef..5b35cf7 100644 --- a/build-aux/qmi-codegen/Message.py +++ b/build-aux/qmi-codegen/Message.py @@ -42,6 +42,8 @@ class Message: self.type = dictionary['type'] # The version info, optional self.version_info = dictionary['version'].split('.') if 'version' in dictionary else [] + self.static = True if 'scope' in dictionary and dictionary['scope'] == 'library-only' else False + self.abort = True if 'abort' in dictionary and dictionary['abort'] == 'yes' else False # The message prefix self.prefix = 'Qmi ' + self.type @@ -61,7 +63,8 @@ class Message: self.output = Container(self.fullname, 'Output', dictionary['output'] if 'output' in dictionary else None, - common_objects_dictionary) + common_objects_dictionary, + self.static) self.input = None if self.type == 'Message': @@ -72,7 +75,8 @@ class Message: self.input = Container(self.fullname, 'Input', dictionary['input'] if 'input' in dictionary else None, - common_objects_dictionary) + common_objects_dictionary, + self.static) """ @@ -400,6 +404,8 @@ class Message: Emit the sections """ def emit_sections(self, sfile): + if self.static: + return translations = { 'hyphened' : utils.build_dashed_name (self.fullname), 'fullname_underscore' : utils.build_underscore_name(self.fullname), diff --git a/data/qmi-service-nas.json b/data/qmi-service-nas.json index e6b8e2b..c3b70ec 100644 --- a/data/qmi-service-nas.json +++ b/data/qmi-service-nas.json @@ -24,6 +24,21 @@ "version" : "1.0", "output" : [ { "common-ref" : "Operation Result" } ] }, + // ********************************************************************************* + { "name" : "Abort", + "type" : "Message", + "service" : "NAS", + "id" : "0x0001", + "version" : "1.0", + // This magic tag allows us to avoid creating a method in the client + "scope" : "library-only", + "input" : [ { "name" : "Transaction ID", + "id" : "0x01", + "mandatory" : "yes", + "type" : "TLV", + "format" : "guint16" } ], + "output" : [ { "common-ref" : "Operation Result" } ] }, + // ********************************************************************************* { "name" : "Set Event Report", "type" : "Message", diff --git a/data/qmi-service-wds.json b/data/qmi-service-wds.json index d0d64ef..081aa87 100644 --- a/data/qmi-service-wds.json +++ b/data/qmi-service-wds.json @@ -20,6 +20,21 @@ "version" : "1.0", "output" : [ { "common-ref" : "Operation Result" } ] }, + // ********************************************************************************* + { "name" : "Abort", + "type" : "Message", + "service" : "WDS", + "id" : "0x0002", + "version" : "1.0", + // This magic tag allows us to avoid creating a method in the client + "scope" : "library-only", + "input" : [ { "name" : "Transaction ID", + "id" : "0x01", + "mandatory" : "yes", + "type" : "TLV", + "format" : "guint16" } ], + "output" : [ { "common-ref" : "Operation Result" } ] }, + // ********************************************************************************* { "name" : "Start Network", "type" : "Message", diff --git a/libqmi-glib/generated/Makefile.am b/libqmi-glib/generated/Makefile.am index d984ce8..f7edabc 100644 --- a/libqmi-glib/generated/Makefile.am +++ b/libqmi-glib/generated/Makefile.am @@ -176,7 +176,8 @@ libqmi_glib_generated_la_CPPFLAGS = \ $(LIBQMI_GLIB_CFLAGS) \ -I$(top_srcdir) \ -I$(top_srcdir)/libqmi-glib \ - -DLIBQMI_GLIB_COMPILATION + -DLIBQMI_GLIB_COMPILATION \ + -Wno-unused-function libqmi_glib_generated_la_LIBADD = \ $(LIBQMI_GLIB_LIBS) -- cgit v1.2.3