diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2016-06-06 22:15:55 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2016-07-07 19:51:28 +0200 |
commit | f5cd7476f4bcd3961d408b603344ad5fff03e40d (patch) | |
tree | c6b1eae2f5cee8a6e2c186c2e004a85a03ab0603 | |
parent | 8080be8209c406169f24d52b69ddda4dd8485c8c (diff) | |
download | ModemManager-f5cd7476f4bcd3961d408b603344ad5fff03e40d.tar.gz |
broadband-modem-mbim: try to use FCC Auth through QMI-over-MBIM if power up fails
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | src/mm-broadband-modem-mbim.c | 445 |
2 files changed, 382 insertions, 67 deletions
diff --git a/configure.ac b/configure.ac index 1065f08c8..a5ab780e8 100644 --- a/configure.ac +++ b/configure.ac @@ -269,7 +269,7 @@ dnl----------------------------------------------------------------------------- dnl MBIM support (enabled by default) dnl -LIBMBIM_VERSION=1.12.0 +LIBMBIM_VERSION=1.14.0 AC_ARG_WITH(mbim, AS_HELP_STRING([--without-mbim], [Build without MBIM support]), [], [with_mbim=yes]) AM_CONDITIONAL(WITH_MBIM, test "x$with_mbim" = "xyes") @@ -293,7 +293,7 @@ dnl----------------------------------------------------------------------------- dnl QMI support (enabled by default) dnl -LIBQMI_VERSION=1.14.0 +LIBQMI_VERSION=1.16.0 AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [], [with_qmi=yes]) AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes") diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c index 603809478..f1ddd6c39 100644 --- a/src/mm-broadband-modem-mbim.c +++ b/src/mm-broadband-modem-mbim.c @@ -38,6 +38,10 @@ #include "mm-iface-modem-messaging.h" #include "mm-sms-part-3gpp.h" +#if defined WITH_QMI +# include <libqmi-glib.h> +#endif + static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); @@ -939,43 +943,246 @@ modem_load_power_state (MMIfaceModem *self, } /*****************************************************************************/ -/* Power up/down (Modem interface) */ +/* Power up (Modem interface) */ + +typedef enum { + POWER_UP_CONTEXT_STEP_FIRST, +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + POWER_UP_CONTEXT_STEP_QMI_DEVICE_NEW, + POWER_UP_CONTEXT_STEP_QMI_DEVICE_OPEN, + POWER_UP_CONTEXT_STEP_ALLOCATE_QMI_CLIENT_DMS, + POWER_UP_CONTEXT_STEP_FCC_AUTH, + POWER_UP_CONTEXT_STEP_RELEASE_QMI_CLIENT_DMS, + POWER_UP_CONTEXT_STEP_RETRY, +#endif + POWER_UP_CONTEXT_STEP_LAST, +} PowerUpContextStep; + +typedef struct { + MMBroadbandModemMbim *self; + MbimDevice *device; + GSimpleAsyncResult *result; + PowerUpContextStep step; +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + QmiDevice *qmi_device; + QmiClient *qmi_client; + GError *saved_error; +#endif +} PowerUpContext; + +static void +power_up_context_complete_and_free (PowerUpContext *ctx) +{ +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + if (ctx->qmi_device) { + if (ctx->qmi_client) { + qmi_device_release_client (ctx->qmi_device, + ctx->qmi_client, + QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, + 3, NULL, NULL, NULL); + g_object_unref (ctx->qmi_client); + } + g_object_unref (ctx->qmi_device); + } + if (ctx->saved_error) + g_error_free (ctx->saved_error); +#endif + g_simple_async_result_complete (ctx->result); + g_object_unref (ctx->result); + g_object_unref (ctx->device); + g_object_unref (ctx->self); + g_slice_free (PowerUpContext, ctx); +} static gboolean -common_power_up_down_finish (MMIfaceModem *self, - GAsyncResult *res, - GError **error) +power_up_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) { return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } +static void power_up_context_step (PowerUpContext *ctx); + +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + static void -radio_state_set_down_ready (MbimDevice *device, - GAsyncResult *res, - GSimpleAsyncResult *simple) +release_qmi_client_dms_ready (QmiDevice *dev, + GAsyncResult *res, + PowerUpContext *ctx) { - MbimMessage *response; GError *error = NULL; - response = mbim_device_command_finish (device, res, &error); - if (response) - mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); + /* Non-fatal error */ + if (!qmi_device_release_client_finish (dev, res, &error)) { + g_debug ("error: couldn't release client: %s", error->message); + g_error_free (error); + } - if (error) - g_simple_async_result_take_error (simple, error); - else - g_simple_async_result_set_op_res_gboolean (simple, TRUE); + ctx->step++; + power_up_context_step (ctx); +} - if (response) - mbim_message_unref (response); - g_simple_async_result_complete (simple); - g_object_unref (simple); +static void +set_radio_state_release_qmi_client_dms (PowerUpContext *ctx) +{ + qmi_device_release_client (ctx->qmi_device, + ctx->qmi_client, + QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, + 10, + NULL, + (GAsyncReadyCallback)release_qmi_client_dms_ready, + ctx); } static void -radio_state_set_up_ready (MbimDevice *device, - GAsyncResult *res, - GSimpleAsyncResult *simple) +set_fcc_authentication_ready (QmiClientDms *client, + GAsyncResult *res, + PowerUpContext *ctx) +{ + QmiMessageDmsSetFccAuthenticationOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_fcc_authentication_finish (client, res, &error); + if (!output || !qmi_message_dms_set_fcc_authentication_output_get_result (output, &error)) { + g_debug ("error: couldn't set FCC auth: %s", error->message); + g_error_free (error); + g_assert (ctx->saved_error); + g_simple_async_result_take_error (ctx->result, ctx->saved_error); + ctx->saved_error = NULL; + power_up_context_complete_and_free (ctx); + goto out; + } + + ctx->step++; + power_up_context_step (ctx); + +out: + if (output) + qmi_message_dms_set_fcc_authentication_output_unref (output); +} + +static void +set_radio_state_fcc_auth (PowerUpContext *ctx) +{ + qmi_client_dms_set_fcc_authentication (QMI_CLIENT_DMS (ctx->qmi_client), + NULL, + 10, + NULL, /* cancellable */ + (GAsyncReadyCallback)set_fcc_authentication_ready, + ctx); +} + +static void +qmi_client_dms_ready (QmiDevice *dev, + GAsyncResult *res, + PowerUpContext *ctx) +{ + GError *error = NULL; + + ctx->qmi_client = qmi_device_allocate_client_finish (dev, res, &error); + if (!ctx->qmi_client) { + g_debug ("error: couldn't create DMS client: %s", error->message); + g_error_free (error); + g_assert (ctx->saved_error); + g_simple_async_result_take_error (ctx->result, ctx->saved_error); + ctx->saved_error = NULL; + power_up_context_complete_and_free (ctx); + return; + } + + ctx->step++; + power_up_context_step (ctx); +} + +static void +set_radio_state_allocate_qmi_client_dms (PowerUpContext *ctx) +{ + g_assert (ctx->qmi_device); + qmi_device_allocate_client (ctx->qmi_device, + QMI_SERVICE_DMS, + QMI_CID_NONE, + 10, + NULL, /* cancellable */ + (GAsyncReadyCallback) qmi_client_dms_ready, + ctx); +} + +static void +device_open_ready (QmiDevice *dev, + GAsyncResult *res, + PowerUpContext *ctx) +{ + GError *error = NULL; + + if (!qmi_device_open_finish (dev, res, &error)) { + g_debug ("error: couldn't open QmiDevice: %s", error->message); + g_error_free (error); + g_assert (ctx->saved_error); + g_simple_async_result_take_error (ctx->result, ctx->saved_error); + ctx->saved_error = NULL; + power_up_context_complete_and_free (ctx); + return; + } + + ctx->step++; + power_up_context_step (ctx); +} + +static void +set_radio_state_qmi_device_open (PowerUpContext *ctx) +{ + /* Open the device */ + g_assert (ctx->qmi_device); + qmi_device_open (ctx->qmi_device, + (QMI_DEVICE_OPEN_FLAGS_PROXY | QMI_DEVICE_OPEN_FLAGS_MBIM), + 15, + NULL, /* cancellable */ + (GAsyncReadyCallback)device_open_ready, + ctx); +} + +static void +qmi_device_new_ready (GObject *unused, + GAsyncResult *res, + PowerUpContext *ctx) +{ + GError *error = NULL; + + ctx->qmi_device = qmi_device_new_finish (res, &error); + if (!ctx->qmi_device) { + g_debug ("error: couldn't create QmiDevice: %s", error->message); + g_error_free (error); + g_assert (ctx->saved_error); + g_simple_async_result_take_error (ctx->result, ctx->saved_error); + ctx->saved_error = NULL; + power_up_context_complete_and_free (ctx); + return; + } + + ctx->step++; + power_up_context_step (ctx); +} + +static void +set_radio_state_qmi_device_new (PowerUpContext *ctx) +{ + GFile *file; + + file = mbim_device_get_file (ctx->device); + qmi_device_new (file, + NULL, /* cancellable */ + (GAsyncReadyCallback) qmi_device_new_ready, + ctx); + g_object_unref (file); +} + +#endif + +static void +radio_state_set_up_ready (MbimDevice *device, + GAsyncResult *res, + PowerUpContext *ctx) { MbimMessage *response; GError *error = NULL; @@ -995,64 +1202,99 @@ radio_state_set_up_ready (MbimDevice *device, MM_CORE_ERROR_FAILED, "Cannot power-up: hardware radio switch is OFF"); else if (software_radio_state == MBIM_RADIO_SWITCH_STATE_OFF) - g_warn_if_reached (); + error = g_error_new (MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Cannot power-up: sotware radio switch is OFF"); } - if (error) - g_simple_async_result_take_error (simple, error); - else - g_simple_async_result_set_op_res_gboolean (simple, TRUE); - if (response) mbim_message_unref (response); - g_simple_async_result_complete (simple); - g_object_unref (simple); -} -static void -common_power_up_down (MMIfaceModem *self, - gboolean up, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *result; - MbimDevice *device; - MbimMessage *message; - MbimRadioSwitchState state; - GAsyncReadyCallback ready_cb; + /* Nice! we're done, quick exit */ + if (!error) { + ctx->step = POWER_UP_CONTEXT_STEP_LAST; + power_up_context_step (ctx); + return; + } - if (!peek_device (self, &device, callback, user_data)) +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + /* Only the first attempt isn't fatal */ + if (ctx->step == POWER_UP_CONTEXT_STEP_FIRST) { + /* Warn and keep, will retry */ + g_warning ("%s", error->message); + g_assert (!ctx->saved_error); + ctx->saved_error = error; + ctx->step++; + power_up_context_step (ctx); return; + } +#endif - result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - common_power_up_down); + /* Fatal */ + g_simple_async_result_take_error (ctx->result, error); + power_up_context_complete_and_free (ctx); +} - if (up) { - ready_cb = (GAsyncReadyCallback)radio_state_set_up_ready; - state = MBIM_RADIO_SWITCH_STATE_ON; - } else { - ready_cb = (GAsyncReadyCallback)radio_state_set_down_ready; - state = MBIM_RADIO_SWITCH_STATE_OFF; - } +static void +set_radio_state_up (PowerUpContext *ctx) +{ + MbimMessage *message; - message = mbim_message_radio_state_set_new (state, NULL); - mbim_device_command (device, + message = mbim_message_radio_state_set_new (MBIM_RADIO_SWITCH_STATE_ON, NULL); + mbim_device_command (ctx->device, message, 20, NULL, - ready_cb, - result); + (GAsyncReadyCallback)radio_state_set_up_ready, + ctx); mbim_message_unref (message); } static void -modem_power_down (MMIfaceModem *self, - GAsyncReadyCallback callback, - gpointer user_data) +power_up_context_step (PowerUpContext *ctx) { - common_power_up_down (self, FALSE, callback, user_data); + switch (ctx->step) { + case POWER_UP_CONTEXT_STEP_FIRST: + set_radio_state_up (ctx); + return; + +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + + case POWER_UP_CONTEXT_STEP_QMI_DEVICE_NEW: + set_radio_state_qmi_device_new (ctx); + return; + + case POWER_UP_CONTEXT_STEP_QMI_DEVICE_OPEN: + set_radio_state_qmi_device_open (ctx); + return; + + case POWER_UP_CONTEXT_STEP_ALLOCATE_QMI_CLIENT_DMS: + set_radio_state_allocate_qmi_client_dms (ctx); + return; + + case POWER_UP_CONTEXT_STEP_FCC_AUTH: + set_radio_state_fcc_auth (ctx); + return; + + case POWER_UP_CONTEXT_STEP_RELEASE_QMI_CLIENT_DMS: + set_radio_state_release_qmi_client_dms (ctx); + return; + + case POWER_UP_CONTEXT_STEP_RETRY: + set_radio_state_up (ctx); + return; + +#endif + + case POWER_UP_CONTEXT_STEP_LAST: + /* Good! */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + power_up_context_complete_and_free (ctx); + return; + + default: + g_assert_not_reached (); + } } static void @@ -1060,7 +1302,80 @@ modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { - common_power_up_down (self, TRUE, callback, user_data); + PowerUpContext *ctx; + MbimDevice *device; + + if (!peek_device (self, &device, callback, user_data)) + return; + + ctx = g_slice_new0 (PowerUpContext); + ctx->self = g_object_ref (self); + ctx->device = g_object_ref (device); + ctx->step = POWER_UP_CONTEXT_STEP_FIRST; + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + modem_power_up); + power_up_context_step (ctx); +} + +/*****************************************************************************/ +/* Power down (Modem interface) */ + +static gboolean +power_down_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +radio_state_set_down_ready (MbimDevice *device, + GAsyncResult *res, + GSimpleAsyncResult *simple) +{ + MbimMessage *response; + GError *error = NULL; + + response = mbim_device_command_finish (device, res, &error); + if (response) { + mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); + mbim_message_unref (response); + } + + if (error) + 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); +} + +static void +modem_power_down (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + MbimDevice *device; + MbimMessage *message; + + if (!peek_device (self, &device, callback, user_data)) + return; + + simple = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + modem_power_down); + message = mbim_message_radio_state_set_new (MBIM_RADIO_SWITCH_STATE_OFF, NULL); + mbim_device_command (device, + message, + 20, + NULL, + (GAsyncReadyCallback)radio_state_set_down_ready, + simple); + mbim_message_unref (message); } /*****************************************************************************/ @@ -2903,9 +3218,9 @@ iface_modem_init (MMIfaceModem *iface) iface->load_power_state = modem_load_power_state; iface->load_power_state_finish = modem_load_power_state_finish; iface->modem_power_up = modem_power_up; - iface->modem_power_up_finish = common_power_up_down_finish; + iface->modem_power_up_finish = power_up_finish; iface->modem_power_down = modem_power_down; - iface->modem_power_down_finish = common_power_up_down_finish; + iface->modem_power_down_finish = power_down_finish; iface->load_supported_ip_families = modem_load_supported_ip_families; iface->load_supported_ip_families_finish = modem_load_supported_ip_families_finish; |