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.
This commit is contained in:
parent
5e1507472d
commit
13b53c6a1d
|
@ -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))
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
"""
|
||||
|
|
|
@ -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}'
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue