[PATCH 3/3] broadband-modem-qmi: use "UIM Get Card Status" if "DMS UIM" command unavailable
Aleksander Morgado
aleksander at aleksander.es
Tue Dec 29 08:56:56 PST 2015
The newer modules like the MC7455 which support multiple SIM cards won't
implement the legacy "DMS UIM" commands.
---
src/mm-broadband-modem-qmi.c | 452 +++++++++++++++++++++++++++++++++++++------
1 file changed, 390 insertions(+), 62 deletions(-)
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index f738ba1..999ead1 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -90,6 +90,9 @@ struct _MMBroadbandModemQmiPrivate {
guint signal_info_indication_id;
#endif /* WITH_NEWEST_QMI_COMMANDS */
+ /* New devices may not support the legacy DMS UIM Get PIN status */
+ gboolean dms_uim_get_pin_status_supported;
+
/* 3GPP/CDMA registration helpers */
gchar *current_operator_id;
gchar *current_operator_description;
@@ -1426,6 +1429,30 @@ modem_load_own_numbers (MMIfaceModem *self,
/*****************************************************************************/
/* Check if unlock required (Modem interface) */
+typedef enum {
+ LOAD_UNLOCK_REQUIRED_STEP_FIRST,
+ LOAD_UNLOCK_REQUIRED_STEP_CDMA,
+ LOAD_UNLOCK_REQUIRED_STEP_DMS,
+ LOAD_UNLOCK_REQUIRED_STEP_UIM,
+} LoadUnlockRequiredStep;
+
+typedef struct {
+ MMBroadbandModemQmi *self;
+ GSimpleAsyncResult *result;
+ LoadUnlockRequiredStep step;
+ QmiClient *dms;
+ QmiClient *uim;
+} LoadUnlockRequiredContext;
+
+static void
+load_unlock_required_context_complete_and_free (LoadUnlockRequiredContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->self);
+ g_slice_free (LoadUnlockRequiredContext, ctx);
+}
+
static MMModemLock
modem_load_unlock_required_finish (MMIfaceModem *self,
GAsyncResult *res,
@@ -1438,19 +1465,271 @@ modem_load_unlock_required_finish (MMIfaceModem *self,
G_SIMPLE_ASYNC_RESULT (res)));
}
+static void load_unlock_required_context_step (LoadUnlockRequiredContext *ctx);
+
+static void
+uim_get_card_status_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ LoadUnlockRequiredContext *ctx)
+{
+ QmiMessageUimGetCardStatusOutput *output;
+ GError *error = NULL;
+ GArray *cards;
+ QmiMessageUimGetCardStatusOutputCardStatusCardsElement *card;
+ QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElement *app;
+ MMModemLock lock = MM_MODEM_LOCK_UNKNOWN;
+ guint i;
+ gint card_i = -1;
+ gint application_j = -1;
+ guint n_absent = 0;
+ guint n_error = 0;
+ guint n_invalid = 0;
+
+ /* This command supports MULTIPLE cards with MULTIPLE applications each. For our
+ * purposes, we're going to consider as the SIM to use the first card present
+ * with a SIM/USIM application. */
+
+ output = qmi_client_uim_get_card_status_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_simple_async_result_take_error (ctx->result, error);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ if (!qmi_message_uim_get_card_status_output_get_result (output, &error)) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_simple_async_result_take_error (ctx->result, error);
+ qmi_message_uim_get_card_status_output_unref (output);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ qmi_message_uim_get_card_status_output_get_card_status (
+ output,
+ NULL, /* index_gw_primary */
+ NULL, /* index_1x_primary */
+ NULL, /* index_gw_secondary */
+ NULL, /* index_1x_secondary */
+ &cards,
+ NULL);
+
+ if (cards->len == 0) {
+ g_simple_async_result_set_error (ctx->result, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED,
+ "No cards reported");
+ qmi_message_uim_get_card_status_output_unref (output);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ if (cards->len > 1)
+ g_debug ("Multiple cards reported: %u", cards->len);
+
+ /* All KNOWN applications in all cards will need to be in READY state for us
+ * to consider UNLOCKED */
+ for (i = 0; i < cards->len; i++) {
+ card = &g_array_index (cards, QmiMessageUimGetCardStatusOutputCardStatusCardsElement, i);
+
+ switch (card->card_state) {
+ case QMI_UIM_CARD_STATE_PRESENT: {
+ guint j;
+ gboolean sim_usim_found = FALSE;
+
+ if (card->applications->len == 0) {
+ g_debug ("No applications reported in card [%u]", i);
+ n_invalid++;
+ break;
+ }
+
+ if (card->applications->len > 1)
+ g_debug ("Multiple applications reported in card [%u]: %u", i, card->applications->len);
+
+ for (j = 0; j < card->applications->len; j++) {
+ app = &g_array_index (card->applications, QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElement, j);
+
+ if (app->type == QMI_UIM_CARD_APPLICATION_TYPE_UNKNOWN) {
+ g_debug ("Unknown application [%u] found in card [%u]: %s. Ignored.",
+ j, i, qmi_uim_card_application_state_get_string (app->state));
+ continue;
+ }
+
+ g_debug ("Application '%s' [%u] in card [%u]: %s",
+ qmi_uim_card_application_type_get_string (app->type), j, i, qmi_uim_card_application_state_get_string (app->state));
+
+ if (app->type == QMI_UIM_CARD_APPLICATION_TYPE_SIM || app->type == QMI_UIM_CARD_APPLICATION_TYPE_USIM) {
+ /* We found the card/app pair to use! Only keep the first found,
+ * but still, keep on looping to log about the remaining ones */
+ if (card_i < 0 && application_j < 0) {
+ card_i = i;
+ application_j = j;
+ }
+
+ sim_usim_found = TRUE;
+ }
+ }
+
+ if (!sim_usim_found) {
+ g_debug ("No SIM/USIM application found in card [%u]", i);
+ n_invalid++;
+ }
+
+ break;
+ }
+
+ case QMI_UIM_CARD_STATE_ABSENT:
+ g_debug ("Card '%u' is absent", i);
+ n_absent++;
+ break;
+
+ case QMI_UIM_CARD_STATE_ERROR:
+ default:
+ n_error++;
+ if (qmi_uim_card_error_get_string (card->error_code) != NULL)
+ g_warning ("Card '%u' is unusable: %s", i, qmi_uim_card_error_get_string (card->error_code));
+ else
+ g_warning ("Card '%u' is unusable: unknown error", i);
+ break;
+ }
+
+ /* go on to next card */
+ }
+
+ /* If we found no card/app to use, we need to report an error */
+ if (card_i < 0 || application_j < 0) {
+ /* If not a single card found, report SIM not inserted */
+ if (n_absent > 0 && !n_error && !n_invalid)
+ g_simple_async_result_set_error (ctx->result,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED,
+ "No card found");
+ else if (n_error > 0)
+ g_simple_async_result_set_error (ctx->result,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG,
+ "Card error");
+ else
+ g_simple_async_result_set_error (ctx->result,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE,
+ "Card failure: %u absent, %u errors, %u invalid",
+ n_absent, n_error, n_invalid);
+ qmi_message_uim_get_card_status_output_unref (output);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Get card/app to use */
+ card = &g_array_index (cards, QmiMessageUimGetCardStatusOutputCardStatusCardsElement, card_i);
+ app = &g_array_index (card->applications, QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElement, application_j);
+
+ /* If card not ready yet, return RETRY error */
+ if (app->state != QMI_UIM_CARD_APPLICATION_STATE_READY) {
+ g_debug ("Neither SIM nor USIM are ready");
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_RETRY,
+ "SIM not ready yet (retry)");
+ qmi_message_uim_get_card_status_output_unref (output);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Card is ready, what's the lock status? */
+
+ /* PIN1 */
+ switch (app->pin1_state) {
+ case QMI_UIM_PIN_STATE_PERMANENTLY_BLOCKED:
+ g_simple_async_result_set_error (ctx->result,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG,
+ "SIM PIN/PUK permanently blocked");
+ qmi_message_uim_get_card_status_output_unref (output);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+
+ case QMI_UIM_PIN_STATE_ENABLED_NOT_VERIFIED:
+ lock = MM_MODEM_LOCK_SIM_PIN;
+ break;
+
+ case QMI_UIM_PIN_STATE_BLOCKED:
+ lock = MM_MODEM_LOCK_SIM_PUK;
+ break;
+
+ case QMI_UIM_PIN_STATE_DISABLED:
+ case QMI_UIM_PIN_STATE_ENABLED_VERIFIED:
+ lock = MM_MODEM_LOCK_NONE;
+ break;
+
+ default:
+ g_simple_async_result_set_error (ctx->result,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG,
+ "Unknown SIM PIN/PUK status");
+ qmi_message_uim_get_card_status_output_unref (output);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* PIN2 */
+ if (lock == MM_MODEM_LOCK_NONE) {
+ switch (app->pin2_state) {
+ case QMI_UIM_PIN_STATE_ENABLED_NOT_VERIFIED:
+ lock = MM_MODEM_LOCK_SIM_PIN2;
+ break;
+
+ case QMI_UIM_PIN_STATE_PERMANENTLY_BLOCKED:
+ g_warning ("PUK2 permanently blocked");
+ case QMI_UIM_PIN_STATE_BLOCKED:
+ lock = MM_MODEM_LOCK_SIM_PUK2;
+ break;
+
+ case QMI_UIM_PIN_STATE_DISABLED:
+ case QMI_UIM_PIN_STATE_ENABLED_VERIFIED:
+ break;
+
+ default:
+ g_warning ("Unknown SIM PIN2/PUK2 status");
+ break;
+ }
+ }
+
+ g_simple_async_result_set_op_res_gpointer (ctx->result, GUINT_TO_POINTER (lock), NULL);
+ qmi_message_uim_get_card_status_output_unref (output);
+ load_unlock_required_context_complete_and_free (ctx);
+}
+
static void
dms_uim_get_pin_status_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ LoadUnlockRequiredContext *ctx)
{
QmiMessageDmsUimGetPinStatusOutput *output;
GError *error = NULL;
+ MMModemLock lock = MM_MODEM_LOCK_UNKNOWN;
+ QmiDmsUimPinStatus current_status;
output = qmi_client_dms_uim_get_pin_status_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
- } else if (!qmi_message_dms_uim_get_pin_status_output_get_result (output, &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ if (!qmi_message_dms_uim_get_pin_status_output_get_result (output, &error)) {
+ /* We get InvalidQmiCommand on newer devices which don't like the legacy way */
+ if (g_error_matches (error,
+ QMI_PROTOCOL_ERROR,
+ QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) {
+ g_error_free (error);
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
+ /* Flag that the command is unsupported, and try with the new way */
+ ctx->self->priv->dms_uim_get_pin_status_supported = FALSE;
+ ctx->step++;
+ load_unlock_required_context_step (ctx);
+ return;
+ }
+
/* Internal and uim-uninitialized errors are retry-able before being fatal */
if (g_error_matches (error,
QMI_PROTOCOL_ERROR,
@@ -1458,85 +1737,130 @@ dms_uim_get_pin_status_ready (QmiClientDms *client,
g_error_matches (error,
QMI_PROTOCOL_ERROR,
QMI_PROTOCOL_ERROR_UIM_UNINITIALIZED)) {
- g_simple_async_result_set_error (simple,
+ g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_RETRY,
"Couldn't get PIN status (retry): %s",
error->message);
g_error_free (error);
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
}
- /* Other errors, just propagate them */
- else {
- g_prefix_error (&error, "Couldn't get PIN status: ");
- g_simple_async_result_take_error (simple, error);
- }
- } else {
- MMModemLock lock = MM_MODEM_LOCK_UNKNOWN;
- QmiDmsUimPinStatus current_status;
- if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status (
- output,
- ¤t_status,
- NULL, /* verify_retries_left */
- NULL, /* unblock_retries_left */
- NULL))
- lock = mm_modem_lock_from_qmi_uim_pin_status (current_status, TRUE);
+ /* Other errors, just propagate them */
+ g_prefix_error (&error, "Couldn't get PIN status: ");
+ g_simple_async_result_take_error (ctx->result, error);
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
- if (lock == MM_MODEM_LOCK_NONE &&
- qmi_message_dms_uim_get_pin_status_output_get_pin2_status (
- output,
- ¤t_status,
- NULL, /* verify_retries_left */
- NULL, /* unblock_retries_left */
- NULL))
- lock = mm_modem_lock_from_qmi_uim_pin_status (current_status, FALSE);
+ /* Command succeeded, process results */
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (lock), NULL);
- }
+ if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status (
+ output,
+ ¤t_status,
+ NULL, /* verify_retries_left */
+ NULL, /* unblock_retries_left */
+ NULL))
+ lock = mm_modem_lock_from_qmi_uim_pin_status (current_status, TRUE);
- if (output)
- qmi_message_dms_uim_get_pin_status_output_unref (output);
+ if (lock == MM_MODEM_LOCK_NONE &&
+ qmi_message_dms_uim_get_pin_status_output_get_pin2_status (
+ output,
+ ¤t_status,
+ NULL, /* verify_retries_left */
+ NULL, /* unblock_retries_left */
+ NULL))
+ lock = mm_modem_lock_from_qmi_uim_pin_status (current_status, FALSE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ /* We're done! */
+ g_simple_async_result_set_op_res_gpointer (ctx->result, GUINT_TO_POINTER (lock), NULL);
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
+ load_unlock_required_context_complete_and_free (ctx);
}
static void
-modem_load_unlock_required (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_unlock_required_context_step (LoadUnlockRequiredContext *ctx)
{
- GSimpleAsyncResult *result;
- QmiClient *client = NULL;
+ GError *error = NULL;
+ QmiClient *client;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
- return;
+ switch (ctx->step) {
+ case LOAD_UNLOCK_REQUIRED_STEP_FIRST:
+ ctx->step++;
+ /* Go on to next step */
+
+ case LOAD_UNLOCK_REQUIRED_STEP_CDMA:
+ /* CDMA-only modems don't need this */
+ if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (ctx->self))) {
+ mm_dbg ("Skipping unlock check in CDMA-only modem...");
+ g_simple_async_result_set_op_res_gpointer (ctx->result, GUINT_TO_POINTER (MM_MODEM_LOCK_NONE), NULL);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+ ctx->step++;
+ /* Go on to next step */
+
+ case LOAD_UNLOCK_REQUIRED_STEP_DMS:
+ if (ctx->self->priv->dms_uim_get_pin_status_supported) {
+ /* Failure to get DMS client is hard really */
+ client = peek_qmi_client (ctx->self, QMI_SERVICE_DMS, &error);
+ if (!client) {
+ g_simple_async_result_take_error (ctx->result, error);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_unlock_required);
+ mm_dbg ("loading unlock required (DMS)...");
+ qmi_client_dms_uim_get_pin_status (QMI_CLIENT_DMS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) dms_uim_get_pin_status_ready,
+ ctx);
+ return;
+ }
+ ctx->step++;
+ /* Go on to next step */
- /* CDMA-only modems don't need this */
- if (mm_iface_modem_is_cdma_only (self)) {
- mm_dbg ("Skipping unlock check in CDMA-only modem...");
- g_simple_async_result_set_op_res_gpointer (result,
- GUINT_TO_POINTER (MM_MODEM_LOCK_NONE),
- NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ case LOAD_UNLOCK_REQUIRED_STEP_UIM:
+ /* Failure to get UIM client at this point is hard as well */
+ client = peek_qmi_client (ctx->self, QMI_SERVICE_UIM, &error);
+ if (!client) {
+ g_simple_async_result_take_error (ctx->result, error);
+ load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ mm_dbg ("loading unlock required (UIM)...");
+ qmi_client_uim_get_card_status (QMI_CLIENT_UIM (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) uim_get_card_status_ready,
+ ctx);
return;
}
+}
- mm_dbg ("loading unlock required...");
- qmi_client_dms_uim_get_pin_status (QMI_CLIENT_DMS (client),
- NULL,
- 5,
- NULL,
- (GAsyncReadyCallback)dms_uim_get_pin_status_ready,
- result);
+static void
+modem_load_unlock_required (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ LoadUnlockRequiredContext *ctx;
+
+ ctx = g_slice_new0 (LoadUnlockRequiredContext);
+ ctx->self = g_object_ref (self);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ modem_load_unlock_required);
+ ctx->step = LOAD_UNLOCK_REQUIRED_STEP_FIRST;
+
+ load_unlock_required_context_step (ctx);
}
/*****************************************************************************/
@@ -10841,7 +11165,8 @@ initialization_started (MMBroadbandModem *self,
ctx->services[2] = QMI_SERVICE_WMS;
ctx->services[3] = QMI_SERVICE_PDS;
ctx->services[4] = QMI_SERVICE_OMA;
- ctx->services[5] = QMI_SERVICE_UNKNOWN;
+ ctx->services[5] = QMI_SERVICE_UIM;
+ ctx->services[6] = QMI_SERVICE_UNKNOWN;
/* Now open our QMI port */
mm_port_qmi_open (ctx->qmi,
@@ -10876,6 +11201,9 @@ mm_broadband_modem_qmi_init (MMBroadbandModemQmi *self)
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_BROADBAND_MODEM_QMI,
MMBroadbandModemQmiPrivate);
+
+ /* Initially we always try to use the legacy command */
+ self->priv->dms_uim_get_pin_status_supported = TRUE;
}
static void
--
2.6.4
More information about the ModemManager-devel
mailing list