diff options
-rw-r--r-- | cli/qmicli-wds.c | 29 | ||||
-rw-r--r-- | src/qmi-client-wds.c | 30 | ||||
-rw-r--r-- | src/qmi-client-wds.h | 17 | ||||
-rw-r--r-- | src/qmi-message-wds.c | 446 | ||||
-rw-r--r-- | src/qmi-message-wds.h | 10 | ||||
-rw-r--r-- | src/qmi-wds.h | 30 |
6 files changed, 495 insertions, 67 deletions
diff --git a/cli/qmicli-wds.c b/cli/qmicli-wds.c index f9f443e..7c13e35 100644 --- a/cli/qmicli-wds.c +++ b/cli/qmicli-wds.c @@ -174,14 +174,34 @@ start_network_ready (QmiClientWds *client, GAsyncResult *res) { GError *error = NULL; + QmiWdsStartNetworkOutput *output; - ctx->packet_data_handle = qmi_client_wds_start_network_finish (client, res, &error); - if (!ctx->packet_data_handle) { - g_printerr ("error: couldn't start network: %s\n", + output = qmi_client_wds_start_network_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); exit (EXIT_FAILURE); } + if (!qmi_wds_start_network_output_get_result (output, &error)) { + g_printerr ("error: couldn't start network: %s\n", error->message); + if (g_error_matches (error, + QMI_PROTOCOL_ERROR, + QMI_PROTOCOL_ERROR_CALL_FAILED)) { + guint16 cer; + guint16 domain; + + if (qmi_wds_start_network_output_get_call_end_reason (output, &cer)) + g_printerr ("call end reason: %u\n", cer); + if (qmi_wds_start_network_output_get_verbose_call_end_reason (output, &cer, &domain)) + g_printerr ("verbose call end reason: %u, %u\n", domain, cer); + } + + exit (EXIT_FAILURE); + } + + qmi_wds_start_network_output_get_packet_data_handle (output, &ctx->packet_data_handle); + #undef VALIDATE_UNKNOWN #define VALIDATE_UNKNOWN(str) (str ? str : "unknown") @@ -195,6 +215,8 @@ start_network_ready (QmiClientWds *client, G_CALLBACK (network_cancelled), NULL, NULL); + + qmi_wds_start_network_output_unref (output); } static void @@ -214,6 +236,7 @@ allocate_client_ready (QmiDevice *device, if (start_network_flag) { g_debug ("Asynchronously starting network..."); qmi_client_wds_start_network (ctx->client, + NULL, /* allow NULL input for now */ 10, ctx->cancellable, (GAsyncReadyCallback)start_network_ready, diff --git a/src/qmi-client-wds.c b/src/qmi-client-wds.c index 6752526..3f784b5 100644 --- a/src/qmi-client-wds.c +++ b/src/qmi-client-wds.c @@ -30,15 +30,15 @@ G_DEFINE_TYPE (QmiClientWds, qmi_client_wds, QMI_TYPE_CLIENT); /*****************************************************************************/ /* Start network */ -guint32 +QmiWdsStartNetworkOutput * qmi_client_wds_start_network_finish (QmiClientWds *self, GAsyncResult *res, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) - return 0; /* TODO: what would be a good invalid packet data handle? */ + return NULL; - return (guint32) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); + return qmi_wds_start_network_output_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); } static void @@ -48,7 +48,7 @@ start_network_ready (QmiDevice *device, { GError *error = NULL; QmiMessage *reply; - guint32 result; + QmiWdsStartNetworkOutput *output; reply = qmi_device_command_finish (device, res, &error); if (!reply) { @@ -59,19 +59,20 @@ start_network_ready (QmiDevice *device, } /* Parse reply */ - result = qmi_message_wds_start_network_reply_parse (reply, &error); - if (!result) + output = qmi_message_wds_start_network_reply_parse (reply, &error); + if (!output) g_simple_async_result_take_error (simple, error); else g_simple_async_result_set_op_res_gpointer (simple, - GUINT_TO_POINTER (result), - NULL); + output, + (GDestroyNotify)qmi_wds_start_network_output_unref); g_simple_async_result_complete (simple); g_object_unref (simple); } void qmi_client_wds_start_network (QmiClientWds *self, + QmiWdsStartNetworkInput *input, guint timeout, GCancellable *cancellable, GAsyncReadyCallback callback, @@ -79,6 +80,7 @@ qmi_client_wds_start_network (QmiClientWds *self, { GSimpleAsyncResult *result; QmiMessage *request; + GError *error = NULL; result = g_simple_async_result_new (G_OBJECT (self), callback, @@ -86,7 +88,17 @@ qmi_client_wds_start_network (QmiClientWds *self, qmi_client_wds_start_network); request = qmi_message_wds_start_network_new (qmi_client_get_next_transaction_id (QMI_CLIENT (self)), - qmi_client_get_cid (QMI_CLIENT (self))); + qmi_client_get_cid (QMI_CLIENT (self)), + input, + &error); + if (!request) { + g_prefix_error (&error, "Couldn't create request message: "); + g_simple_async_result_take_error (result, error); + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + return; + } + qmi_device_command (qmi_client_peek_device (QMI_CLIENT (self)), request, timeout, diff --git a/src/qmi-client-wds.h b/src/qmi-client-wds.h index 7ae0b40..7e81521 100644 --- a/src/qmi-client-wds.h +++ b/src/qmi-client-wds.h @@ -53,14 +53,15 @@ GType qmi_client_wds_get_type (void); /*****************************************************************************/ /* Start network */ -void qmi_client_wds_start_network (QmiClientWds *self, - guint timeout, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -guint32 qmi_client_wds_start_network_finish (QmiClientWds *self, - GAsyncResult *res, - GError **error); +void qmi_client_wds_start_network (QmiClientWds *self, + QmiWdsStartNetworkInput *input, + guint timeout, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +QmiWdsStartNetworkOutput *qmi_client_wds_start_network_finish (QmiClientWds *self, + GAsyncResult *res, + GError **error); /*****************************************************************************/ /* Stop network */ diff --git a/src/qmi-message-wds.c b/src/qmi-message-wds.c index a2b4d0b..74efaa4 100644 --- a/src/qmi-message-wds.c +++ b/src/qmi-message-wds.c @@ -21,6 +21,8 @@ * Copyright (C) 2012 Aleksander Morgado <aleksander@lanedo.com> */ +#include <string.h> + #include "qmi-message-wds.h" #include "qmi-enums.h" #include "qmi-error-types.h" @@ -28,15 +30,227 @@ /*****************************************************************************/ /* Start network */ +enum { + QMI_WDS_TLV_START_NETWORK_APN = 0x14, + QMI_WDS_TLV_START_NETWORK_USERNAME = 0x17, + QMI_WDS_TLV_START_NETWORK_PASSWORD = 0x18, +}; + +/** + * QmiWdsStartNetworkInput: + * + * An opaque type handling the input arguments that may be passed to the Start + * Network operation in the WDS service. + */ +struct _QmiWdsStartNetworkInput { + volatile gint ref_count; + gchar *apn; + gchar *username; + gchar *password; +}; + +/** + * qmi_wds_start_network_input_set_apn: + * @input: a #QmiWdsStartNetworkInput. + * @str: a string with the APN. + * + * Set the APN to use. + */ +void +qmi_wds_start_network_input_set_apn (QmiWdsStartNetworkInput *input, + const gchar *str) +{ + g_return_if_fail (input != NULL); + + g_free (input->apn); + input->apn = (str ? g_strdup (str) : NULL); +} + +/** + * qmi_wds_start_network_input_get_apn: + * @input: a #QmiWdsStartNetworkInput. + * + * Get the configured APN to use. + * + * Returns: a constant string with the APN, or #NULL if none set. + */ +const gchar * +qmi_wds_start_network_input_get_apn (QmiWdsStartNetworkInput *input) +{ + g_return_val_if_fail (input != NULL, NULL); + + return input->apn; +} + +/** + * qmi_wds_start_network_input_set_username: + * @input: a #QmiWdsStartNetworkInput. + * @str: a string with the username. + * + * Set the username to use when authenticating with the network. + */ +void +qmi_wds_start_network_input_set_username (QmiWdsStartNetworkInput *input, + const gchar *str) +{ + g_return_if_fail (input != NULL); + + g_free (input->username); + input->username = (str ? g_strdup (str) : NULL); +} + +/** + * qmi_wds_start_network_input_get_username: + * @input: a #QmiWdsStartNetworkInput. + * + * Get the configured username to use when authenticating with the network. + * + * Returns: a constant string with the username, or #NULL if none set. + */ +const gchar * +qmi_wds_start_network_input_get_username (QmiWdsStartNetworkInput *input) +{ + g_return_val_if_fail (input != NULL, NULL); + + return input->username; +} + +/** + * qmi_wds_start_network_input_set_password: + * @input: a #QmiWdsStartNetworkInput. + * @str: a string with the password. + * + * Set the password to use when authenticating with the network. + */ +void +qmi_wds_start_network_input_set_password (QmiWdsStartNetworkInput *input, + const gchar *str) +{ + g_return_if_fail (input != NULL); + + g_free (input->password); + input->password = (str ? g_strdup (str) : NULL); +} + +/** + * qmi_wds_start_network_input_get_password: + * @input: a #QmiWdsStartNetworkInput. + * + * Get the configured password to use when authenticating with the network. + * + * Returns: a constant string with the password, or #NULL if none set. + */ +const gchar * +qmi_wds_start_network_input_get_password (QmiWdsStartNetworkInput *input) +{ + g_return_val_if_fail (input != NULL, NULL); + + return input->password; +} + +/** + * qmi_wds_start_network_input_new: + * + * Allocates a new #QmiWdsStartNetworkInput. + * + * Returns: the newly created #QmiWdsStartNetworkInput. + */ +QmiWdsStartNetworkInput * +qmi_wds_start_network_input_new (void) +{ + return g_slice_new0 (QmiWdsStartNetworkInput); +} + +/** + * qmi_wds_start_network_input_ref: + * @input: a #QmiWdsStartNetworkInput. + * + * Atomically increments the reference count of @input by one. + * + * Returns: the new reference to @input. + */ +QmiWdsStartNetworkInput * +qmi_wds_start_network_input_ref (QmiWdsStartNetworkInput *input) +{ + g_return_val_if_fail (input != NULL, NULL); + + g_atomic_int_inc (&input->ref_count); + return input; +} + +/** + * qmi_wds_start_network_input_unref: + * @input: a #QmiWdsStartNetworkInput. + * + * Atomically decrements the reference count of @input by one. + * If the reference count drops to 0, @input is completely disposed. + */ +void +qmi_wds_start_network_input_unref (QmiWdsStartNetworkInput *input) +{ + g_return_if_fail (input != NULL); + + if (g_atomic_int_dec_and_test (&input->ref_count)) { + g_free (input->apn); + g_free (input->username); + g_free (input->password); + g_slice_free (QmiWdsStartNetworkInput, input); + } +} + QmiMessage * qmi_message_wds_start_network_new (guint8 transaction_id, - guint8 client_id) + guint8 client_id, + QmiWdsStartNetworkInput *input, + GError **error) { - /* TODO: handle optional TLVs */ - return qmi_message_new (QMI_SERVICE_WDS, - client_id, - transaction_id, - QMI_WDS_MESSAGE_START_NETWORK); + QmiMessage *message; + + message = qmi_message_new (QMI_SERVICE_WDS, + client_id, + transaction_id, + QMI_WDS_MESSAGE_START_NETWORK); + + /* If input arguments given, use them */ + if (input) { + /* Add APN if any */ + if (input->apn && + !qmi_message_tlv_add (message, + QMI_WDS_TLV_START_NETWORK_APN, + strlen (input->apn) + 1, + input->apn, + error)) { + g_prefix_error (error, "Failed to add APN to message: "); + qmi_message_unref (message); + return NULL; + } + + /* Add username if any */ + if (input->username && + !qmi_message_tlv_add (message, + QMI_WDS_TLV_START_NETWORK_USERNAME, + strlen (input->username) + 1, + input->username, + error)) { + g_prefix_error (error, "Failed to add username to message: "); + qmi_message_unref (message); + return NULL; + } + + /* Add password if any */ + if (input->password && + !qmi_message_tlv_add (message, + QMI_WDS_TLV_START_NETWORK_PASSWORD, + strlen (input->password) + 1, + input->password, + error)) { + g_prefix_error (error, "Failed to add password to message: "); + qmi_message_unref (message); + return NULL; + } + } + + return message; } enum { @@ -45,81 +259,227 @@ enum { START_NETWORK_OUTPUT_TLV_VERBOSE_CALL_END_REASON = 0x11 }; -struct verbose_call_end_reason { - guint16 call_end_reason_type; - guint16 call_end_reason; -} __attribute__((__packed__)); +/** + * QmiWdsStartNetworkOutput: + * + * An opaque type handling the output of the Start Network operation. + */ +struct _QmiWdsStartNetworkOutput { + volatile gint ref_count; + GError *error; + + gboolean packet_data_handle_set; + guint32 packet_data_handle; + + gboolean call_end_reason_set; + guint16 call_end_reason; + + gboolean verbose_call_end_reason_set; + guint16 verbose_call_end_reason_domain; + guint16 verbose_call_end_reason_value; +}; + +/** + * qmi_wds_start_network_output_get_result: + * @output: a #QmiWdsStartNetworkOutput. + * @error: a #GError. + * + * Get the result of the Start Network operation. + * + * Returns: #TRUE if the operation succeeded, and #FALSE if @error is set. + */ +gboolean +qmi_wds_start_network_output_get_result (QmiWdsStartNetworkOutput *output, + GError **error) +{ + g_return_val_if_fail (output != NULL, FALSE); + + if (output->error) { + if (error) + *error = g_error_copy (output->error); + + return FALSE; + } + + return TRUE; +} + +/** + * qmi_wds_start_network_output_get_packet_data_handle: + * @output: a #QmiWdsStartNetworkOutput. + * @packet_data_handle: location for the output packet data handle. + * + * Get the packet data handle on a successful Start Network operation. + * + * Returns: #TRUE if @packet_data_handle is set, #FALSE otherwise. + */ +gboolean +qmi_wds_start_network_output_get_packet_data_handle (QmiWdsStartNetworkOutput *output, + guint32 *packet_data_handle) +{ + g_return_val_if_fail (output != NULL, FALSE); + + if (output->packet_data_handle_set) + *packet_data_handle = output->packet_data_handle; + return output->packet_data_handle_set; +} + +/** + * qmi_wds_start_network_output_get_call_end_reason: + * @output: a #QmiWdsStartNetworkOutput. + * @call_end_reason: location for the output call end reason. + * + * Get the call end reason, if the operation failed with a CALL_FAILED error. + * This field is optional. + * + * Returns: #TRUE if @call_end_reason is set, #FALSE otherwise. + */ +gboolean +qmi_wds_start_network_output_get_call_end_reason (QmiWdsStartNetworkOutput *output, + guint16 *call_end_reason) +{ + g_return_val_if_fail (output != NULL, FALSE); + + if (output->call_end_reason_set) + *call_end_reason = output->call_end_reason; + return output->call_end_reason_set; +} + +/** + * qmi_wds_start_network_output_get_call_end_reason: + * @output: a #QmiWdsStartNetworkOutput. + * @verbose_call_end_reason_domain: location for the output call end reason domain. + * @verbose_call_end_reason_value: location for the output call end reason value. + * + * Get the verbose call end reason, if the operation failed with a CALL_FAILED error. + * This field is optional. + * + * Returns: #TRUE if @call_end_reason is set, #FALSE otherwise. + */ +gboolean +qmi_wds_start_network_output_get_verbose_call_end_reason (QmiWdsStartNetworkOutput *output, + guint16 *verbose_call_end_reason_domain, + guint16 *verbose_call_end_reason_value) +{ + g_return_val_if_fail (output != NULL, FALSE); + + if (output->verbose_call_end_reason_set) { + *verbose_call_end_reason_domain = output->verbose_call_end_reason_domain; + *verbose_call_end_reason_value = output->verbose_call_end_reason_value; + } + return output->call_end_reason_set; +} + +/** + * qmi_wds_start_network_output_ref: + * @output: a #QmiWdsStartNetworkOutput. + * + * Atomically increments the reference count of @output by one. + * + * Returns: the new reference to @output. + */ +QmiWdsStartNetworkOutput * +qmi_wds_start_network_output_ref (QmiWdsStartNetworkOutput *output) +{ + g_return_val_if_fail (output != NULL, NULL); + + g_atomic_int_inc (&output->ref_count); + return output; +} + +/** + * qmi_wds_start_network_output_unref: + * @output: a #QmiWdsStartNetworkOutput. + * + * Atomically decrements the reference count of @output by one. + * If the reference count drops to 0, @output is completely disposed. + */ +void +qmi_wds_start_network_output_unref (QmiWdsStartNetworkOutput *output) +{ + g_return_if_fail (output != NULL); -guint32 + if (g_atomic_int_dec_and_test (&output->ref_count)) { + if (output->error) + g_error_free (output->error); + g_slice_free (QmiWdsStartNetworkOutput, output); + } +} + +QmiWdsStartNetworkOutput * qmi_message_wds_start_network_reply_parse (QmiMessage *self, GError **error) { + QmiWdsStartNetworkOutput *output; GError *inner_error = NULL; - guint32 packet_data_handle = 0; g_assert (qmi_message_get_message_id (self) == QMI_WDS_MESSAGE_START_NETWORK); - /* If we got a QMI error reported and is a CALL_FAILED one, try to gather - * the call end reason */ if (!qmi_message_get_response_result (self, &inner_error)) { - /* On CALL_FAILED errors, we can try to get more info on the reason */ - if (g_error_matches (inner_error, + /* Only QMI protocol errors are set in the Output result, all the + * others (e.g. failures parsing) are directly propagated to error. */ + if (inner_error->domain != QMI_PROTOCOL_ERROR) { + g_propagate_error (error, inner_error); + return NULL; + } + + /* Otherwise, build output */ + } + + output = g_slice_new0 (QmiWdsStartNetworkOutput); + output->error = inner_error; + + /* On CALL_FAILED errors, we can try to get more info on the reason */ + if (output->error) { + if (g_error_matches (output->error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_CALL_FAILED)) { guint16 cer = 0; - struct verbose_call_end_reason verbose_cer = { 0, 0 }; + struct verbose_call_end_reason { + guint16 call_end_reason_domain; + guint16 call_end_reason_value; + } __attribute__((__packed__)) verbose_cer = { 0, 0 }; /* TODO: Prepare an enum with all the possible call end reasons, * in order to do this nicely */ - /* Try to get the verbose reason first */ if (qmi_message_tlv_get (self, START_NETWORK_OUTPUT_TLV_VERBOSE_CALL_END_REASON, sizeof (verbose_cer), &verbose_cer, NULL)) { - g_set_error (error, - QMI_PROTOCOL_ERROR, - QMI_PROTOCOL_ERROR_CALL_FAILED, - "Call end reason: %u, %u", - verbose_cer.call_end_reason_type, - verbose_cer.call_end_reason); - g_error_free (inner_error); - return 0; + output->verbose_call_end_reason_set = TRUE; + output->verbose_call_end_reason_domain = verbose_cer.call_end_reason_domain; + output->verbose_call_end_reason_value = verbose_cer.call_end_reason_value; } - /* If no verbose reason, try to use the legacy one */ if (qmi_message_tlv_get (self, START_NETWORK_OUTPUT_TLV_CALL_END_REASON, sizeof (cer), &cer, NULL)) { - g_set_error (error, - QMI_PROTOCOL_ERROR, - QMI_PROTOCOL_ERROR_CALL_FAILED, - "Call end reason: %u", - cer); - g_error_free (inner_error); - return 0; + output->call_end_reason_set = TRUE; + output->call_end_reason = cer; } - - /* otherwise, fall down and propagate the error */ } - g_propagate_error (error, inner_error); - return 0; + return output; } + /* success */ + if (!qmi_message_tlv_get (self, START_NETWORK_OUTPUT_TLV_PACKET_DATA_HANDLE, - sizeof (packet_data_handle), - &packet_data_handle, + sizeof (output->packet_data_handle), + &output->packet_data_handle, error)) { g_prefix_error (error, "Couldn't get the packet data handle TLV: "); - return 0; - } + qmi_wds_start_network_output_unref (output); + return NULL; + } else + output->packet_data_handle_set = TRUE; - return packet_data_handle; + return output; } /*****************************************************************************/ diff --git a/src/qmi-message-wds.h b/src/qmi-message-wds.h index 4318741..b8b80f8 100644 --- a/src/qmi-message-wds.h +++ b/src/qmi-message-wds.h @@ -35,10 +35,12 @@ G_BEGIN_DECLS /*****************************************************************************/ /* Start network */ -QmiMessage *qmi_message_wds_start_network_new (guint8 transaction_id, - guint8 client_id); -guint32 qmi_message_wds_start_network_reply_parse (QmiMessage *self, - GError **error); +QmiMessage *qmi_message_wds_start_network_new (guint8 transaction_id, + guint8 client_id, + QmiWdsStartNetworkInput *input, + GError **error); +QmiWdsStartNetworkOutput *qmi_message_wds_start_network_reply_parse (QmiMessage *self, + GError **error); /*****************************************************************************/ /* Stop network */ diff --git a/src/qmi-wds.h b/src/qmi-wds.h index 0ea8eac..5c3dcf1 100644 --- a/src/qmi-wds.h +++ b/src/qmi-wds.h @@ -37,6 +37,36 @@ typedef enum { QMI_WDS_MESSAGE_PACKET_STATUS = 0x0022, /* unused currently */ } QmiWdsMessage; +/*****************************************************************************/ +/* Start network */ + +typedef struct _QmiWdsStartNetworkInput QmiWdsStartNetworkInput; +QmiWdsStartNetworkInput *qmi_wds_start_network_input_new (void); +QmiWdsStartNetworkInput *qmi_wds_start_network_input_ref (QmiWdsStartNetworkInput *input); +void qmi_wds_start_network_input_unref (QmiWdsStartNetworkInput *input); +void qmi_wds_start_network_input_set_apn (QmiWdsStartNetworkInput *input, + const gchar *str); +const gchar *qmi_wds_start_network_input_get_apn (QmiWdsStartNetworkInput *input); +void qmi_wds_start_network_input_set_username (QmiWdsStartNetworkInput *input, + const gchar *str); +const gchar *qmi_wds_start_network_input_get_username (QmiWdsStartNetworkInput *input); +void qmi_wds_start_network_input_set_password (QmiWdsStartNetworkInput *input, + const gchar *str); +const gchar *qmi_wds_start_network_input_get_password (QmiWdsStartNetworkInput *input); + +typedef struct _QmiWdsStartNetworkOutput QmiWdsStartNetworkOutput; +QmiWdsStartNetworkOutput *qmi_wds_start_network_output_ref (QmiWdsStartNetworkOutput *output); +void qmi_wds_start_network_output_unref (QmiWdsStartNetworkOutput *output); +gboolean qmi_wds_start_network_output_get_result (QmiWdsStartNetworkOutput *output, + GError **error); +gboolean qmi_wds_start_network_output_get_packet_data_handle (QmiWdsStartNetworkOutput *output, + guint32 *packet_data_handle); +/* TODO: provide proper enums for the call end reasons */ +gboolean qmi_wds_start_network_output_get_call_end_reason (QmiWdsStartNetworkOutput *output, + guint16 *call_end_reason); +gboolean qmi_wds_start_network_output_get_verbose_call_end_reason (QmiWdsStartNetworkOutput *output, + guint16 *verbose_call_end_reason_domain, + guint16 *verbose_call_end_reason_value); G_END_DECLS |